Migrate votingpool tests to use wtxmgr
This commit is contained in:
parent
56039deb94
commit
847c7d8df9
7 changed files with 316 additions and 327 deletions
|
@ -26,11 +26,11 @@ import (
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/votingpool"
|
"github.com/btcsuite/btcwallet/votingpool"
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
|
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -166,8 +166,7 @@ func Example_empowerSeries() {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
// This example demonstrates how to empower a series by loading the private
|
// This example demonstrates how to use the Pool.StartWithdrawal method.
|
||||||
// key for one of the series' public keys.
|
|
||||||
func Example_startWithdrawal() {
|
func Example_startWithdrawal() {
|
||||||
// Create the address manager and votingpool DB namespace. See the example
|
// Create the address manager and votingpool DB namespace. See the example
|
||||||
// for the Create() function for more info on how this is done.
|
// 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
|
return pool, seriesID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func exampleCreateTxStore() (*txstore.Store, func(), error) {
|
func exampleCreateTxStore() (*wtxmgr.Store, func(), error) {
|
||||||
dir, err := ioutil.TempDir("", "tx.bin")
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
s := txstore.New(dir)
|
|
||||||
return s, func() { os.RemoveAll(dir) }, nil
|
return s, func() { os.RemoveAll(dir) }, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,15 +25,16 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcutil/hdkeychain"
|
"github.com/btcsuite/btcutil/hdkeychain"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -51,11 +52,10 @@ func getUniqueID() uint32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// createWithdrawalTx creates a withdrawalTx with the given input and output amounts.
|
// createWithdrawalTx creates a withdrawalTx with the given input and output amounts.
|
||||||
func createWithdrawalTx(t *testing.T, pool *Pool, store *txstore.Store, inputAmounts []int64,
|
func createWithdrawalTx(t *testing.T, pool *Pool, inputAmounts []int64, outputAmounts []int64) *withdrawalTx {
|
||||||
outputAmounts []int64) *withdrawalTx {
|
|
||||||
net := pool.Manager().ChainParams()
|
net := pool.Manager().ChainParams()
|
||||||
tx := newWithdrawalTx()
|
tx := newWithdrawalTx()
|
||||||
_, credits := TstCreateCredits(t, pool, inputAmounts, store)
|
_, credits := TstCreateCreditsOnNewSeries(t, pool, inputAmounts)
|
||||||
for _, c := range credits {
|
for _, c := range credits {
|
||||||
tx.addInput(c)
|
tx.addInput(c)
|
||||||
}
|
}
|
||||||
|
@ -136,12 +136,23 @@ func TstCreatePkScript(t *testing.T, p *Pool, seriesID uint32, branch Branch, id
|
||||||
return pkScript
|
return pkScript
|
||||||
}
|
}
|
||||||
|
|
||||||
func TstCreateTxStore(t *testing.T) (store *txstore.Store, tearDown func()) {
|
func TstCreateTxStore(t *testing.T) (store *wtxmgr.Store, tearDown func()) {
|
||||||
dir, err := ioutil.TempDir("", "tx.bin")
|
dir, err := ioutil.TempDir("", "pool_test_txstore")
|
||||||
if err != nil {
|
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) }
|
return s, func() { os.RemoveAll(dir) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,15 +212,12 @@ func TstCreateSeriesDef(t *testing.T, pool *Pool, reqSigs uint32, keys []*hdkeyc
|
||||||
pubkey, _ := key.Neuter()
|
pubkey, _ := key.Neuter()
|
||||||
pubKeys[i] = pubkey.String()
|
pubKeys[i] = pubkey.String()
|
||||||
}
|
}
|
||||||
seriesID := uint32(len(pool.seriesLookup))
|
seriesID := uint32(len(pool.seriesLookup)) + 1
|
||||||
if seriesID == 0 {
|
|
||||||
seriesID++
|
|
||||||
}
|
|
||||||
return TstSeriesDef{
|
return TstSeriesDef{
|
||||||
ReqSigs: reqSigs, SeriesID: seriesID, PubKeys: pubKeys, PrivKeys: privKeys}
|
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)
|
mgrTearDown, _, pool := TstCreatePool(t)
|
||||||
store, storeTearDown := TstCreateTxStore(t)
|
store, storeTearDown := TstCreateTxStore(t)
|
||||||
tearDown = func() {
|
tearDown = func() {
|
||||||
|
@ -219,12 +227,11 @@ func TstCreatePoolAndTxStore(t *testing.T) (tearDown func(), pool *Pool, store *
|
||||||
return tearDown, pool, store
|
return tearDown, pool, store
|
||||||
}
|
}
|
||||||
|
|
||||||
// TstCreateCredits creates a new Series (with a unique ID) and a slice of
|
// TstCreateCreditsOnNewSeries creates a new Series (with a unique ID) and a
|
||||||
// credits locked to the series' address with branch==1 and index==0. The new
|
// slice of credits locked to the series' address with branch==1 and index==0.
|
||||||
// Series will use a 2-of-3 configuration and will be empowered with all of its
|
// The new Series will use a 2-of-3 configuration and will be empowered with
|
||||||
// private keys.
|
// all of its private keys.
|
||||||
func TstCreateCredits(t *testing.T, pool *Pool, amounts []int64, store *txstore.Store) (
|
func TstCreateCreditsOnNewSeries(t *testing.T, pool *Pool, amounts []int64) (uint32, []credit) {
|
||||||
uint32, []Credit) {
|
|
||||||
masters := []*hdkeychain.ExtendedKey{
|
masters := []*hdkeychain.ExtendedKey{
|
||||||
TstCreateMasterKey(t, bytes.Repeat(uint32ToBytes(getUniqueID()), 4)),
|
TstCreateMasterKey(t, bytes.Repeat(uint32ToBytes(getUniqueID()), 4)),
|
||||||
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)
|
def := TstCreateSeriesDef(t, pool, 2, masters)
|
||||||
TstCreateSeries(t, pool, []TstSeriesDef{def})
|
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
|
// TstCreateSeriesCredits creates a new credit for every item in the amounts
|
||||||
// series' address with branch==1 and index==0.
|
// slice, locked to the given series' address with branch==1 and index==0.
|
||||||
func TstCreateCreditsOnSeries(t *testing.T, pool *Pool, seriesID uint32, amounts []int64,
|
func TstCreateSeriesCredits(t *testing.T, pool *Pool, seriesID uint32, amounts []int64) []credit {
|
||||||
store *txstore.Store) []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)
|
branch := Branch(1)
|
||||||
idx := Index(0)
|
idx := Index(0)
|
||||||
pkScript := TstCreatePkScript(t, pool, seriesID, branch, idx)
|
pkScript := TstCreatePkScript(t, pool, seriesID, branch, idx)
|
||||||
eligible := make([]Credit, len(amounts))
|
eligible := make([]credit, len(amounts))
|
||||||
for i, credit := range TstCreateInputs(t, store, pkScript, amounts) {
|
for i, credit := range TstCreateCreditsOnStore(t, store, pkScript, amounts) {
|
||||||
eligible[i] = newCredit(credit, *TstNewWithdrawalAddress(t, pool, seriesID, branch, idx))
|
eligible[i] = newCredit(credit, *TstNewWithdrawalAddress(t, pool, seriesID, branch, idx))
|
||||||
}
|
}
|
||||||
return eligible
|
return eligible
|
||||||
}
|
}
|
||||||
|
|
||||||
// TstCreateInputs is a convenience function. See TstCreateInputsOnBlock
|
// TstCreateCreditsOnStore inserts a new credit in the given store for
|
||||||
// for a more flexible version.
|
// every item in the amounts slice.
|
||||||
func TstCreateInputs(t *testing.T, store *txstore.Store, pkScript []byte, amounts []int64) []txstore.Credit {
|
func TstCreateCreditsOnStore(t *testing.T, s *wtxmgr.Store, pkScript []byte,
|
||||||
return TstCreateInputsOnBlock(t, store, 1, pkScript, amounts)
|
amounts []int64) []wtxmgr.Credit {
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
msgTx := createMsgTx(pkScript, amounts)
|
msgTx := createMsgTx(pkScript, amounts)
|
||||||
block := &txstore.Block{
|
meta := &wtxmgr.BlockMeta{
|
||||||
Height: TstInputsBlock,
|
Block: wtxmgr.Block{Height: TstInputsBlock},
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := btcutil.NewTx(msgTx)
|
rec, err := wtxmgr.NewTxRecordFromMsgTx(msgTx, time.Now())
|
||||||
tx.SetIndex(blockTxIndex)
|
|
||||||
|
|
||||||
r, err := s.InsertTx(tx, block)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.InsertTx(rec, meta); err != nil {
|
||||||
t.Fatal("Failed to create inputs: ", err)
|
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 {
|
for i := range msgTx.TxOut {
|
||||||
credit, err := r.AddCredit(uint32(i), false)
|
if err := s.AddCredit(rec, meta, uint32(i), false); err != nil {
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to create inputs: ", err)
|
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
|
return credits
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,61 +22,31 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
const eligibleInputMinConfirmations = 100
|
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.
|
// 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 {
|
type credit struct {
|
||||||
txstore.Credit
|
wtxmgr.Credit
|
||||||
addr WithdrawalAddress
|
addr WithdrawalAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
// newCredit initialises a new credit.
|
func newCredit(c wtxmgr.Credit, addr WithdrawalAddress) credit {
|
||||||
func newCredit(c txstore.Credit, addr WithdrawalAddress) *credit {
|
return credit{Credit: c, addr: addr}
|
||||||
return &credit{Credit: c, addr: addr}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *credit) String() string {
|
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
|
// byAddress defines the methods needed to satisify sort.Interface to sort a
|
||||||
// slice of Credits by their address.
|
// slice of credits by their address.
|
||||||
type byAddress []Credit
|
type byAddress []credit
|
||||||
|
|
||||||
func (c byAddress) Len() int { return len(c) }
|
func (c byAddress) Len() int { return len(c) }
|
||||||
func (c byAddress) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
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,
|
// the lexicographic ordering defined on the tuple (SeriesID, Index,
|
||||||
// Branch, TxSha, OutputIndex).
|
// Branch, TxSha, OutputIndex).
|
||||||
func (c byAddress) Less(i, j int) bool {
|
func (c byAddress) Less(i, j int) bool {
|
||||||
iAddr := c[i].Address()
|
iAddr := c[i].addr
|
||||||
jAddr := c[j].Address()
|
jAddr := c[j].addr
|
||||||
if iAddr.seriesID < jAddr.seriesID {
|
if iAddr.seriesID < jAddr.seriesID {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -112,7 +82,7 @@ func (c byAddress) Less(i, j int) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The seriesID, index, and branch are equal, so compare hash.
|
// 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 {
|
if txidComparison < 0 {
|
||||||
return true
|
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
|
// The seriesID, index, branch, and hash are equal, so compare output
|
||||||
// index.
|
// 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
|
// getEligibleInputs returns eligible inputs with addresses between startAddress
|
||||||
// and the last used address of lastSeriesID.
|
// 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,
|
lastSeriesID uint32, dustThreshold btcutil.Amount, chainHeight int32,
|
||||||
minConf int) ([]Credit, error) {
|
minConf int) ([]credit, error) {
|
||||||
|
|
||||||
if p.Series(lastSeriesID) == nil {
|
if p.Series(lastSeriesID) == nil {
|
||||||
str := fmt.Sprintf("lastSeriesID (%d) does not exist", lastSeriesID)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var inputs []Credit
|
var inputs []credit
|
||||||
address := startAddress
|
address := startAddress
|
||||||
for {
|
for {
|
||||||
log.Debugf("Looking for eligible inputs at address %v", address.addrIdentifier())
|
log.Debugf("Looking for eligible inputs at address %v", address.addrIdentifier())
|
||||||
if candidates, ok := addrMap[address.addr.EncodeAddress()]; ok {
|
if candidates, ok := addrMap[address.addr.EncodeAddress()]; ok {
|
||||||
var eligibles []Credit
|
var eligibles []credit
|
||||||
for _, c := range candidates {
|
for _, c := range candidates {
|
||||||
if p.isCreditEligible(c, minConf, chainHeight, dustThreshold) {
|
candidate := newCredit(c, address)
|
||||||
eligibles = append(eligibles, newCredit(c, address))
|
if p.isCreditEligible(candidate, minConf, chainHeight, dustThreshold) {
|
||||||
|
eligibles = append(eligibles, candidate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Make sure the eligibles are correctly sorted.
|
// 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
|
// groupCreditsByAddr converts a slice of credits to a map from the string
|
||||||
// representation of an encoded address to the unspent outputs associated with
|
// representation of an encoded address to the unspent outputs associated with
|
||||||
// that address.
|
// that address.
|
||||||
func groupCreditsByAddr(credits []txstore.Credit, chainParams *chaincfg.Params) (
|
func groupCreditsByAddr(credits []wtxmgr.Credit, chainParams *chaincfg.Params) (
|
||||||
map[string][]txstore.Credit, error) {
|
map[string][]wtxmgr.Credit, error) {
|
||||||
addrMap := make(map[string][]txstore.Credit)
|
addrMap := make(map[string][]wtxmgr.Credit)
|
||||||
for _, c := range credits {
|
for _, c := range credits {
|
||||||
_, addrs, _, err := c.Addresses(chainParams)
|
_, addrs, _, err := txscript.ExtractPkScriptAddrs(c.PkScript, chainParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newError(ErrInputSelection, "failed to obtain input address", err)
|
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 {
|
if v, ok := addrMap[encAddr]; ok {
|
||||||
addrMap[encAddr] = append(v, c)
|
addrMap[encAddr] = append(v, c)
|
||||||
} else {
|
} 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
|
// isCreditEligible tests a given credit for eligibilty with respect
|
||||||
// to number of confirmations, the dust threshold and that it is not
|
// to number of confirmations, the dust threshold and that it is not
|
||||||
// the charter output.
|
// 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 {
|
dustThreshold btcutil.Amount) bool {
|
||||||
if c.Amount() < dustThreshold {
|
if c.Amount < dustThreshold {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !c.Confirmed(minConf, chainHeight) {
|
if confirms(c.BlockMeta.Block.Height, chainHeight) < int32(minConf) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if p.isCharterOutput(c) {
|
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
|
// 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.
|
// 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
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -48,17 +48,16 @@ func TestGetEligibleInputs(t *testing.T) {
|
||||||
// Create two eligible inputs locked to each of the PKScripts above.
|
// Create two eligible inputs locked to each of the PKScripts above.
|
||||||
expNoEligibleInputs := 2 * len(scripts)
|
expNoEligibleInputs := 2 * len(scripts)
|
||||||
eligibleAmounts := []int64{int64(dustThreshold + 1), int64(dustThreshold + 1)}
|
eligibleAmounts := []int64{int64(dustThreshold + 1), int64(dustThreshold + 1)}
|
||||||
var inputs []txstore.Credit
|
var inputs []wtxmgr.Credit
|
||||||
for i := 0; i < len(scripts); i++ {
|
for i := 0; i < len(scripts); i++ {
|
||||||
txIndex := int(i) + 1
|
created := TstCreateCreditsOnStore(t, store, scripts[i], eligibleAmounts)
|
||||||
created := TstCreateInputsOnBlock(t, store, txIndex, scripts[i], eligibleAmounts)
|
|
||||||
inputs = append(inputs, created...)
|
inputs = append(inputs, created...)
|
||||||
}
|
}
|
||||||
|
|
||||||
startAddr := TstNewWithdrawalAddress(t, pool, 1, 0, 0)
|
startAddr := TstNewWithdrawalAddress(t, pool, 1, 0, 0)
|
||||||
lastSeriesID := uint32(2)
|
lastSeriesID := uint32(2)
|
||||||
currentBlock := int32(TstInputsBlock + eligibleInputMinConfirmations + 1)
|
currentBlock := int32(TstInputsBlock + eligibleInputMinConfirmations + 1)
|
||||||
var eligibles []Credit
|
var eligibles []credit
|
||||||
var err error
|
var err error
|
||||||
TstRunWithManagerUnlocked(t, pool.Manager(), func() {
|
TstRunWithManagerUnlocked(t, pool.Manager(), func() {
|
||||||
eligibles, err = pool.getEligibleInputs(
|
eligibles, err = pool.getEligibleInputs(
|
||||||
|
@ -213,23 +212,14 @@ func TestNextAddr(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEligibleInputsAreEligible(t *testing.T) {
|
func TestEligibleInputsAreEligible(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
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
|
var chainHeight int32 = 1000
|
||||||
c := TstCreateInputs(t, store, pkScript, []int64{int64(dustThreshold)})[0]
|
_, credits := TstCreateCreditsOnNewSeries(t, pool, []int64{int64(dustThreshold)})
|
||||||
|
c := credits[0]
|
||||||
// Make sure credits is old enough to pass the minConf check.
|
// Make sure credit is old enough to pass the minConf check.
|
||||||
c.BlockHeight = int32(eligibleInputMinConfirmations)
|
c.BlockMeta.Height = int32(eligibleInputMinConfirmations)
|
||||||
|
|
||||||
if !pool.isCreditEligible(c, eligibleInputMinConfirmations, chainHeight, dustThreshold) {
|
if !pool.isCreditEligible(c, eligibleInputMinConfirmations, chainHeight, dustThreshold) {
|
||||||
t.Errorf("Input is not eligible and it should be.")
|
t.Errorf("Input is not eligible and it should be.")
|
||||||
|
@ -237,36 +227,28 @@ func TestEligibleInputsAreEligible(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNonEligibleInputsAreNotEligible(t *testing.T) {
|
func TestNonEligibleInputsAreNotEligible(t *testing.T) {
|
||||||
tearDown, pool, store1 := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
store2, storeTearDown2 := TstCreateTxStore(t)
|
|
||||||
defer tearDown()
|
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
|
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.
|
// Check that credit below dustThreshold is rejected.
|
||||||
c1 := TstCreateInputs(t, store1, pkScript, []int64{int64(dustThreshold - 1)})[0]
|
if pool.isCreditEligible(c, eligibleInputMinConfirmations, chainHeight, dustThreshold) {
|
||||||
c1.BlockHeight = int32(100) // make sure it has enough confirmations.
|
|
||||||
if pool.isCreditEligible(c1, eligibleInputMinConfirmations, chainHeight, dustThreshold) {
|
|
||||||
t.Errorf("Input is eligible and it should not be.")
|
t.Errorf("Input is eligible and it should not be.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that a credit with not enough confirmations is rejected.
|
// Check that a credit with not enough confirmations is rejected.
|
||||||
c2 := TstCreateInputs(t, store2, pkScript, []int64{int64(dustThreshold)})[0]
|
_, credits = TstCreateCreditsOnNewSeries(t, pool, []int64{int64(dustThreshold)})
|
||||||
// the calculation of if it has been confirmed does this:
|
c = credits[0]
|
||||||
// chainheigt - bh + 1 >= target, which is quite weird, but the
|
// The calculation of if it has been confirmed does this: chainheigt - bh +
|
||||||
// reason why I need to put 902 as *that* makes 1000 - 902 +1 = 99 >=
|
// 1 >= target, which is quite weird, but the reason why I need to put 902
|
||||||
// 100 false
|
// is *that* makes 1000 - 902 +1 = 99 >= 100 false
|
||||||
c2.BlockHeight = int32(902)
|
c.BlockMeta.Height = int32(902)
|
||||||
if pool.isCreditEligible(c2, eligibleInputMinConfirmations, chainHeight, dustThreshold) {
|
if pool.isCreditEligible(c, eligibleInputMinConfirmations, chainHeight, dustThreshold) {
|
||||||
t.Errorf("Input is eligible and it should not be.")
|
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)
|
shaHash0 := bytes.Repeat([]byte{0}, 32)
|
||||||
shaHash1 := bytes.Repeat([]byte{1}, 32)
|
shaHash1 := bytes.Repeat([]byte{1}, 32)
|
||||||
shaHash2 := bytes.Repeat([]byte{2}, 32)
|
shaHash2 := bytes.Repeat([]byte{2}, 32)
|
||||||
c0 := TstNewFakeCredit(t, pool, 1, 0, 0, shaHash0, 0)
|
c0 := newDummyCredit(t, pool, 1, 0, 0, shaHash0, 0)
|
||||||
c1 := TstNewFakeCredit(t, pool, 1, 0, 0, shaHash0, 1)
|
c1 := newDummyCredit(t, pool, 1, 0, 0, shaHash0, 1)
|
||||||
c2 := TstNewFakeCredit(t, pool, 1, 0, 0, shaHash1, 0)
|
c2 := newDummyCredit(t, pool, 1, 0, 0, shaHash1, 0)
|
||||||
c3 := TstNewFakeCredit(t, pool, 1, 0, 0, shaHash2, 0)
|
c3 := newDummyCredit(t, pool, 1, 0, 0, shaHash2, 0)
|
||||||
c4 := TstNewFakeCredit(t, pool, 1, 0, 1, shaHash0, 0)
|
c4 := newDummyCredit(t, pool, 1, 0, 1, shaHash0, 0)
|
||||||
c5 := TstNewFakeCredit(t, pool, 1, 1, 0, shaHash0, 0)
|
c5 := newDummyCredit(t, pool, 1, 1, 0, shaHash0, 0)
|
||||||
c6 := TstNewFakeCredit(t, pool, 2, 0, 0, shaHash0, 0)
|
c6 := newDummyCredit(t, pool, 2, 0, 0, shaHash0, 0)
|
||||||
|
|
||||||
randomCredits := [][]Credit{
|
randomCredits := [][]credit{
|
||||||
[]Credit{c6, c5, c4, c3, c2, c1, c0},
|
[]credit{c6, c5, c4, c3, c2, c1, c0},
|
||||||
[]Credit{c2, c1, c0, c6, c5, c4, c3},
|
[]credit{c2, c1, c0, c6, c5, c4, c3},
|
||||||
[]Credit{c6, c4, c5, c2, c3, c0, c1},
|
[]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 {
|
for _, random := range randomCredits {
|
||||||
sort.Sort(byAddress(random))
|
sort.Sort(byAddress(random))
|
||||||
|
@ -318,28 +300,11 @@ func TestCreditSortingByAddress(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TstFakeCredit is a structure implementing the Credit interface used to test
|
// newDummyCredit creates a new credit with the given hash and outpointIdx,
|
||||||
// the byAddress sorting. It exists because to test the sorting properly we need
|
// locked to the votingpool address identified by the given
|
||||||
// to be able to set the Credit's TxSha and OutputIndex.
|
// series/index/branch.
|
||||||
type TstFakeCredit struct {
|
func newDummyCredit(t *testing.T, pool *Pool, series uint32, index Index, branch Branch,
|
||||||
addr WithdrawalAddress
|
txSha []byte, outpointIdx uint32) credit {
|
||||||
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 {
|
|
||||||
var hash wire.ShaHash
|
var hash wire.ShaHash
|
||||||
if err := hash.SetBytes(txSha); err != nil {
|
if err := hash.SetBytes(txSha); err != nil {
|
||||||
t.Fatal(err)
|
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.
|
// the set of used addresses as that's a requirement of WithdrawalAddress.
|
||||||
TstEnsureUsedAddr(t, pool, series, branch, index)
|
TstEnsureUsedAddr(t, pool, series, branch, index)
|
||||||
addr := TstNewWithdrawalAddress(t, pool, series, branch, index)
|
addr := TstNewWithdrawalAddress(t, pool, series, branch, index)
|
||||||
return &TstFakeCredit{
|
c := wtxmgr.Credit{
|
||||||
addr: *addr,
|
OutPoint: wire.OutPoint{
|
||||||
txSha: &hash,
|
Hash: hash,
|
||||||
outputIndex: uint32(outputIdx),
|
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) {
|
func checkUniqueness(t *testing.T, credits byAddress) {
|
||||||
type uniq struct {
|
type uniq struct {
|
||||||
series uint32
|
series uint32
|
||||||
|
@ -371,11 +334,11 @@ func checkUniqueness(t *testing.T, credits byAddress) {
|
||||||
uniqMap := make(map[uniq]bool)
|
uniqMap := make(map[uniq]bool)
|
||||||
for _, c := range credits {
|
for _, c := range credits {
|
||||||
u := uniq{
|
u := uniq{
|
||||||
series: c.Address().SeriesID(),
|
series: c.addr.SeriesID(),
|
||||||
branch: c.Address().Branch(),
|
branch: c.addr.Branch(),
|
||||||
index: c.Address().Index(),
|
index: c.addr.Index(),
|
||||||
hash: *c.TxSha(),
|
hash: c.OutPoint.Hash,
|
||||||
outputIndex: c.OutputIndex(),
|
outputIndex: c.OutPoint.Index,
|
||||||
}
|
}
|
||||||
if _, exists := uniqMap[u]; exists {
|
if _, exists := uniqMap[u]; exists {
|
||||||
t.Fatalf("Duplicate found: %v", u)
|
t.Fatalf("Duplicate found: %v", u)
|
||||||
|
|
|
@ -22,12 +22,13 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
"github.com/btcsuite/fastsha256"
|
"github.com/btcsuite/fastsha256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -235,7 +236,7 @@ type withdrawal struct {
|
||||||
status *WithdrawalStatus
|
status *WithdrawalStatus
|
||||||
transactions []*withdrawalTx
|
transactions []*withdrawalTx
|
||||||
pendingRequests []OutputRequest
|
pendingRequests []OutputRequest
|
||||||
eligibleInputs []Credit
|
eligibleInputs []credit
|
||||||
current *withdrawalTx
|
current *withdrawalTx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +264,7 @@ func (o *withdrawalTxOut) pkScript() []byte {
|
||||||
|
|
||||||
// withdrawalTx represents a transaction constructed by the withdrawal process.
|
// withdrawalTx represents a transaction constructed by the withdrawal process.
|
||||||
type withdrawalTx struct {
|
type withdrawalTx struct {
|
||||||
inputs []Credit
|
inputs []credit
|
||||||
outputs []*withdrawalTxOut
|
outputs []*withdrawalTxOut
|
||||||
fee btcutil.Amount
|
fee btcutil.Amount
|
||||||
|
|
||||||
|
@ -288,7 +289,7 @@ func (tx *withdrawalTx) ntxid() Ntxid {
|
||||||
// inputTotal returns the sum amount of all inputs in this tx.
|
// inputTotal returns the sum amount of all inputs in this tx.
|
||||||
func (tx *withdrawalTx) inputTotal() (total btcutil.Amount) {
|
func (tx *withdrawalTx) inputTotal() (total btcutil.Amount) {
|
||||||
for _, input := range tx.inputs {
|
for _, input := range tx.inputs {
|
||||||
total += input.Amount()
|
total += input.Amount
|
||||||
}
|
}
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
@ -319,7 +320,7 @@ func (tx *withdrawalTx) toMsgTx() *wire.MsgTx {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, i := range tx.inputs {
|
for _, i := range tx.inputs {
|
||||||
msgtx.AddTxIn(wire.NewTxIn(i.OutPoint(), []byte{}))
|
msgtx.AddTxIn(wire.NewTxIn(&i.OutPoint, []byte{}))
|
||||||
}
|
}
|
||||||
return msgtx
|
return msgtx
|
||||||
}
|
}
|
||||||
|
@ -339,16 +340,16 @@ func (tx *withdrawalTx) removeOutput() *withdrawalTxOut {
|
||||||
}
|
}
|
||||||
|
|
||||||
// addInput adds a new input to this transaction.
|
// addInput adds a new input to this transaction.
|
||||||
func (tx *withdrawalTx) addInput(input Credit) {
|
func (tx *withdrawalTx) addInput(input credit) {
|
||||||
log.Debugf("Added tx input with amount %v", input.Amount())
|
log.Debugf("Added tx input with amount %v", input.Amount)
|
||||||
tx.inputs = append(tx.inputs, input)
|
tx.inputs = append(tx.inputs, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeInput removes the last added input and returns it.
|
// 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]
|
removed := tx.inputs[len(tx.inputs)-1]
|
||||||
tx.inputs = 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
|
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
|
// The tx needs to have two or more outputs. The case with only one output must
|
||||||
// be handled separately (by the split output procedure).
|
// 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.
|
// Check precondition: At least two outputs are required in the transaction.
|
||||||
if len(tx.outputs) < 2 {
|
if len(tx.outputs) < 2 {
|
||||||
str := fmt.Sprintf("at least two outputs expected; got %d", len(tx.outputs))
|
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()
|
removedOutput := tx.removeOutput()
|
||||||
|
|
||||||
var removedInputs []Credit
|
var removedInputs []credit
|
||||||
// Continue until sum(in) < sum(out) + fee
|
// Continue until sum(in) < sum(out) + fee
|
||||||
for tx.inputTotal() >= tx.outputTotal()+calculateTxFee(tx) {
|
for tx.inputTotal() >= tx.outputTotal()+calculateTxFee(tx) {
|
||||||
removedInputs = append(removedInputs, tx.removeInput())
|
removedInputs = append(removedInputs, tx.removeInput())
|
||||||
|
@ -399,7 +400,7 @@ func (tx *withdrawalTx) rollBackLastOutput() ([]Credit, *withdrawalTxOut, error)
|
||||||
return removedInputs, removedOutput, nil
|
return removedInputs, removedOutput, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWithdrawal(roundID uint32, requests []OutputRequest, inputs []Credit,
|
func newWithdrawal(roundID uint32, requests []OutputRequest, inputs []credit,
|
||||||
changeStart ChangeAddress) *withdrawal {
|
changeStart ChangeAddress) *withdrawal {
|
||||||
outputs := make(map[OutBailmentID]*WithdrawalOutput, len(requests))
|
outputs := make(map[OutBailmentID]*WithdrawalOutput, len(requests))
|
||||||
for _, request := range 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
|
// found at http://opentransactions.org/wiki/index.php/Startwithdrawal
|
||||||
func (p *Pool) StartWithdrawal(roundID uint32, requests []OutputRequest,
|
func (p *Pool) StartWithdrawal(roundID uint32, requests []OutputRequest,
|
||||||
startAddress WithdrawalAddress, lastSeriesID uint32, changeStart ChangeAddress,
|
startAddress WithdrawalAddress, lastSeriesID uint32, changeStart ChangeAddress,
|
||||||
txStore *txstore.Store, chainHeight int32, dustThreshold btcutil.Amount) (
|
txStore *wtxmgr.Store, chainHeight int32, dustThreshold btcutil.Amount) (
|
||||||
*WithdrawalStatus, error) {
|
*WithdrawalStatus, error) {
|
||||||
|
|
||||||
eligible, err := p.getEligibleInputs(txStore, startAddress, lastSeriesID, dustThreshold,
|
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
|
// popInput removes and returns the first input from the stack of eligible
|
||||||
// inputs.
|
// inputs.
|
||||||
func (w *withdrawal) popInput() Credit {
|
func (w *withdrawal) popInput() credit {
|
||||||
input := w.eligibleInputs[0]
|
input := w.eligibleInputs[0]
|
||||||
w.eligibleInputs = w.eligibleInputs[1:]
|
w.eligibleInputs = w.eligibleInputs[1:]
|
||||||
return input
|
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
|
// 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
|
// extra garbage since it always creates a new single-element slice and append
|
||||||
// the rest of the items to it.
|
// the rest of the items to it.
|
||||||
func (w *withdrawal) pushInput(input Credit) {
|
func (w *withdrawal) pushInput(input credit) {
|
||||||
w.eligibleInputs = append([]Credit{input}, w.eligibleInputs...)
|
w.eligibleInputs = append([]credit{input}, w.eligibleInputs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this returns it means we have added an output and the necessary inputs to fulfil that
|
// 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() {
|
func (w *withdrawal) maybeDropRequests() {
|
||||||
inputAmount := btcutil.Amount(0)
|
inputAmount := btcutil.Amount(0)
|
||||||
for _, input := range w.eligibleInputs {
|
for _, input := range w.eligibleInputs {
|
||||||
inputAmount += input.Amount()
|
inputAmount += input.Amount
|
||||||
}
|
}
|
||||||
outputAmount := btcutil.Amount(0)
|
outputAmount := btcutil.Amount(0)
|
||||||
for _, request := range w.pendingRequests {
|
for _, request := range w.pendingRequests {
|
||||||
|
@ -715,7 +716,7 @@ func getRawSigs(transactions []*withdrawalTx) (map[Ntxid]TxSigs, error) {
|
||||||
msgtx := tx.toMsgTx()
|
msgtx := tx.toMsgTx()
|
||||||
ntxid := tx.ntxid()
|
ntxid := tx.ntxid()
|
||||||
for inputIdx, input := range tx.inputs {
|
for inputIdx, input := range tx.inputs {
|
||||||
creditAddr := input.Address()
|
creditAddr := input.addr
|
||||||
redeemScript := creditAddr.redeemScript()
|
redeemScript := creditAddr.redeemScript()
|
||||||
series := creditAddr.series()
|
series := creditAddr.series()
|
||||||
// The order of the raw signatures in the signature script must match the
|
// 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
|
// manager) the redeem script for each of them and constructing the signature
|
||||||
// script using that and the given raw signatures.
|
// script using that and the given raw signatures.
|
||||||
// This function must be called with the manager unlocked.
|
// This function must be called with the manager unlocked.
|
||||||
func SignTx(msgtx *wire.MsgTx, sigs TxSigs, mgr *waddrmgr.Manager, store *txstore.Store) error {
|
func SignTx(msgtx *wire.MsgTx, sigs TxSigs, mgr *waddrmgr.Manager, store *wtxmgr.Store) error {
|
||||||
credits, err := store.FindPreviousCredits(btcutil.NewTx(msgtx))
|
// We use time.Now() here as we're not going to store the new TxRecord
|
||||||
for i, credit := range credits {
|
// anywhere -- we just need it to pass to store.PreviousPkScripts().
|
||||||
if err = signMultiSigUTXO(mgr, msgtx, i, credit.TxOut().PkScript, sigs[i]); err != nil {
|
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
|
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
|
// Notice that we use 73 as the signature length as that's the maximum
|
||||||
// length they may have:
|
// length they may have:
|
||||||
// https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
|
// https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
|
||||||
addr := tx.inputs[i].Address()
|
addr := tx.inputs[i].addr
|
||||||
redeemScriptLen := len(addr.redeemScript())
|
redeemScriptLen := len(addr.redeemScript())
|
||||||
n := wire.VarIntSerializeSize(uint64(redeemScriptLen))
|
n := wire.VarIntSerializeSize(uint64(redeemScriptLen))
|
||||||
sigScriptLen := 1 + (74 * int(addr.series().reqSigs)) + redeemScriptLen + 1 + n
|
sigScriptLen := 1 + (74 * int(addr.series().reqSigs)) + redeemScriptLen + 1 + n
|
||||||
|
|
|
@ -37,7 +37,7 @@ func TestStartWithdrawal(t *testing.T) {
|
||||||
def := vp.TstCreateSeriesDef(t, pool, 2, masters)
|
def := vp.TstCreateSeriesDef(t, pool, 2, masters)
|
||||||
vp.TstCreateSeries(t, pool, []vp.TstSeriesDef{def})
|
vp.TstCreateSeries(t, pool, []vp.TstSeriesDef{def})
|
||||||
// Create eligible inputs and the list of outputs we need to fulfil.
|
// 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"
|
address1 := "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6"
|
||||||
address2 := "3PbExiaztsSYgh6zeMswC49hLUwhTQ86XG"
|
address2 := "3PbExiaztsSYgh6zeMswC49hLUwhTQ86XG"
|
||||||
requests := []vp.OutputRequest{
|
requests := []vp.OutputRequest{
|
||||||
|
|
|
@ -27,14 +27,13 @@ import (
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcutil/hdkeychain"
|
"github.com/btcsuite/btcutil/hdkeychain"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestOutputSplittingNotEnoughInputs checks that an output will get split if we
|
// TestOutputSplittingNotEnoughInputs checks that an output will get split if we
|
||||||
// don't have enough inputs to fulfil it.
|
// don't have enough inputs to fulfil it.
|
||||||
func TestOutputSplittingNotEnoughInputs(t *testing.T) {
|
func TestOutputSplittingNotEnoughInputs(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
net := pool.Manager().ChainParams()
|
net := pool.Manager().ChainParams()
|
||||||
|
@ -47,7 +46,7 @@ func TestOutputSplittingNotEnoughInputs(t *testing.T) {
|
||||||
TstNewOutputRequest(t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", output1Amount, net),
|
TstNewOutputRequest(t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", output1Amount, net),
|
||||||
TstNewOutputRequest(t, 2, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", output2Amount, 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))
|
w := newWithdrawal(0, requests, eligible, *TstNewChangeAddress(t, pool, seriesID, 0))
|
||||||
|
|
||||||
// Trigger an output split because of lack of inputs by forcing a high fee.
|
// 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) {
|
func TestOutputSplittingOversizeTx(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
requestAmount := btcutil.Amount(5)
|
requestAmount := btcutil.Amount(5)
|
||||||
|
@ -89,7 +88,7 @@ func TestOutputSplittingOversizeTx(t *testing.T) {
|
||||||
smallInput := int64(2)
|
smallInput := int64(2)
|
||||||
request := TstNewOutputRequest(
|
request := TstNewOutputRequest(
|
||||||
t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", requestAmount, pool.Manager().ChainParams())
|
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)
|
changeStart := TstNewChangeAddress(t, pool, seriesID, 0)
|
||||||
w := newWithdrawal(0, []OutputRequest{request}, eligible, *changeStart)
|
w := newWithdrawal(0, []OutputRequest{request}, eligible, *changeStart)
|
||||||
restoreCalculateTxFee := replaceCalculateTxFee(TstConstantFee(0))
|
restoreCalculateTxFee := replaceCalculateTxFee(TstConstantFee(0))
|
||||||
|
@ -136,11 +135,11 @@ func TestOutputSplittingOversizeTx(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSplitLastOutputNoOutputs(t *testing.T) {
|
func TestSplitLastOutputNoOutputs(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
w := newWithdrawal(0, []OutputRequest{}, []Credit{}, ChangeAddress{})
|
w := newWithdrawal(0, []OutputRequest{}, []credit{}, ChangeAddress{})
|
||||||
w.current = createWithdrawalTx(t, pool, store, []int64{}, []int64{})
|
w.current = createWithdrawalTx(t, pool, []int64{}, []int64{})
|
||||||
|
|
||||||
err := w.splitLastOutput()
|
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
|
// Check that all outputs requested in a withdrawal match the outputs of the generated
|
||||||
// transaction(s).
|
// transaction(s).
|
||||||
func TestWithdrawalTxOutputs(t *testing.T) {
|
func TestWithdrawalTxOutputs(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
net := pool.Manager().ChainParams()
|
net := pool.Manager().ChainParams()
|
||||||
|
|
||||||
// Create eligible inputs and the list of outputs we need to fulfil.
|
// 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{
|
outputs := []OutputRequest{
|
||||||
TstNewOutputRequest(t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", 3e6, net),
|
TstNewOutputRequest(t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", 3e6, net),
|
||||||
TstNewOutputRequest(t, 2, "3PbExiaztsSYgh6zeMswC49hLUwhTQ86XG", 2e6, net),
|
TstNewOutputRequest(t, 2, "3PbExiaztsSYgh6zeMswC49hLUwhTQ86XG", 2e6, net),
|
||||||
|
@ -174,7 +173,7 @@ func TestWithdrawalTxOutputs(t *testing.T) {
|
||||||
tx := w.transactions[0]
|
tx := w.transactions[0]
|
||||||
// The created tx should include both eligible credits, so we expect it to have
|
// The created tx should include both eligible credits, so we expect it to have
|
||||||
// an input amount of 2e6+4e6 satoshis.
|
// 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))
|
change := inputAmount - (outputs[0].Amount + outputs[1].Amount + calculateTxFee(tx))
|
||||||
expectedOutputs := append(
|
expectedOutputs := append(
|
||||||
outputs, TstNewOutputRequest(t, 3, changeStart.addr.String(), change, net))
|
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
|
// Check that withdrawal.status correctly states that no outputs were fulfilled when we
|
||||||
// don't have enough eligible credits for any of them.
|
// don't have enough eligible credits for any of them.
|
||||||
func TestFulfillRequestsNoSatisfiableOutputs(t *testing.T) {
|
func TestFulfillRequestsNoSatisfiableOutputs(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
seriesID, eligible := TstCreateCredits(t, pool, []int64{1e6}, store)
|
seriesID, eligible := TstCreateCreditsOnNewSeries(t, pool, []int64{1e6})
|
||||||
request := TstNewOutputRequest(
|
request := TstNewOutputRequest(
|
||||||
t, 1, "3Qt1EaKRD9g9FeL2DGkLLswhK1AKmmXFSe", btcutil.Amount(3e6), pool.Manager().ChainParams())
|
t, 1, "3Qt1EaKRD9g9FeL2DGkLLswhK1AKmmXFSe", btcutil.Amount(3e6), pool.Manager().ChainParams())
|
||||||
changeStart := TstNewChangeAddress(t, pool, seriesID, 0)
|
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
|
// Check that some requested outputs are not fulfilled when we don't have credits for all
|
||||||
// of them.
|
// of them.
|
||||||
func TestFulfillRequestsNotEnoughCreditsForAllRequests(t *testing.T) {
|
func TestFulfillRequestsNotEnoughCreditsForAllRequests(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
net := pool.Manager().ChainParams()
|
net := pool.Manager().ChainParams()
|
||||||
|
|
||||||
// Create eligible inputs and the list of outputs we need to fulfil.
|
// 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(
|
out1 := TstNewOutputRequest(
|
||||||
t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", btcutil.Amount(3e6), net)
|
t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", btcutil.Amount(3e6), net)
|
||||||
out2 := TstNewOutputRequest(
|
out2 := TstNewOutputRequest(
|
||||||
|
@ -240,7 +239,7 @@ func TestFulfillRequestsNotEnoughCreditsForAllRequests(t *testing.T) {
|
||||||
tx := w.transactions[0]
|
tx := w.transactions[0]
|
||||||
// The created tx should spend both eligible credits, so we expect it to have
|
// The created tx should spend both eligible credits, so we expect it to have
|
||||||
// an input amount of 2e6+4e6 satoshis.
|
// 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
|
// 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.
|
// output request #3 should not be there because we don't have enough credits.
|
||||||
change := inputAmount - (out1.Amount + out2.Amount + calculateTxFee(tx))
|
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
|
// TestRollbackLastOutput tests the case where we rollback one output
|
||||||
// and one input, such that sum(in) >= sum(out) + fee.
|
// and one input, such that sum(in) >= sum(out) + fee.
|
||||||
func TestRollbackLastOutput(t *testing.T) {
|
func TestRollbackLastOutput(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
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
|
initialInputs := tx.inputs
|
||||||
initialOutputs := tx.outputs
|
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))
|
t.Fatalf("Unexpected number of inputs removed; got %d, want 1", len(removedInputs))
|
||||||
}
|
}
|
||||||
lastInput := initialInputs[len(initialInputs)-1]
|
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)
|
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) {
|
func TestRollbackLastOutputMultipleInputsRolledBack(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
// This tx will need the 3 last inputs to fulfill the second output, so they
|
// 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.
|
// 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
|
initialInputs := tx.inputs
|
||||||
initialOutputs := tx.outputs
|
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))
|
t.Fatalf("Unexpected number of inputs removed; got %d, want 3", len(removedInputs))
|
||||||
}
|
}
|
||||||
for i, amount := range []btcutil.Amount{4, 3, 2} {
|
for i, amount := range []btcutil.Amount{4, 3, 2} {
|
||||||
if removedInputs[i].Amount() != amount {
|
if removedInputs[i].Amount != amount {
|
||||||
t.Fatalf("Unexpected input amount; got %v, want %v", 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
|
// TestRollbackLastOutputNoInputsRolledBack tests the case where we roll back
|
||||||
// one output but don't need to roll back any inputs.
|
// one output but don't need to roll back any inputs.
|
||||||
func TestRollbackLastOutputNoInputsRolledBack(t *testing.T) {
|
func TestRollbackLastOutputNoInputsRolledBack(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{4}, []int64{2, 3})
|
tx := createWithdrawalTx(t, pool, []int64{4}, []int64{2, 3})
|
||||||
initialInputs := tx.inputs
|
initialInputs := tx.inputs
|
||||||
initialOutputs := tx.outputs
|
initialOutputs := tx.outputs
|
||||||
|
|
||||||
|
@ -385,11 +384,11 @@ func TestRollBackLastOutputInsufficientOutputs(t *testing.T) {
|
||||||
// TestRollbackLastOutputWhenNewOutputAdded checks that we roll back the last
|
// TestRollbackLastOutputWhenNewOutputAdded checks that we roll back the last
|
||||||
// output if a tx becomes too big right after we add a new output to it.
|
// output if a tx becomes too big right after we add a new output to it.
|
||||||
func TestRollbackLastOutputWhenNewOutputAdded(t *testing.T) {
|
func TestRollbackLastOutputWhenNewOutputAdded(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
net := pool.Manager().ChainParams()
|
net := pool.Manager().ChainParams()
|
||||||
series, eligible := TstCreateCredits(t, pool, []int64{5, 5}, store)
|
series, eligible := TstCreateCreditsOnNewSeries(t, pool, []int64{5, 5})
|
||||||
requests := []OutputRequest{
|
requests := []OutputRequest{
|
||||||
// This is ordered by bailment ID
|
// This is ordered by bailment ID
|
||||||
TstNewOutputRequest(t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", 1, net),
|
TstNewOutputRequest(t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", 1, net),
|
||||||
|
@ -433,11 +432,11 @@ func TestRollbackLastOutputWhenNewOutputAdded(t *testing.T) {
|
||||||
// TestRollbackLastOutputWhenNewInputAdded checks that we roll back the last
|
// TestRollbackLastOutputWhenNewInputAdded checks that we roll back the last
|
||||||
// output if a tx becomes too big right after we add a new input to it.
|
// output if a tx becomes too big right after we add a new input to it.
|
||||||
func TestRollbackLastOutputWhenNewInputAdded(t *testing.T) {
|
func TestRollbackLastOutputWhenNewInputAdded(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
net := pool.Manager().ChainParams()
|
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{
|
requests := []OutputRequest{
|
||||||
// This is manually ordered by outBailmentIDHash, which is the order in
|
// This is manually ordered by outBailmentIDHash, which is the order in
|
||||||
// which they're going to be fulfilled by w.fulfillRequests().
|
// which they're going to be fulfilled by w.fulfillRequests().
|
||||||
|
@ -488,10 +487,10 @@ func TestRollbackLastOutputWhenNewInputAdded(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithdrawalTxRemoveOutput(t *testing.T) {
|
func TestWithdrawalTxRemoveOutput(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{}, []int64{1, 2})
|
tx := createWithdrawalTx(t, pool, []int64{}, []int64{1, 2})
|
||||||
outputs := tx.outputs
|
outputs := tx.outputs
|
||||||
// Make sure we have created the transaction with the expected
|
// Make sure we have created the transaction with the expected
|
||||||
// outputs.
|
// outputs.
|
||||||
|
@ -516,10 +515,10 @@ func TestWithdrawalTxRemoveOutput(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithdrawalTxRemoveInput(t *testing.T) {
|
func TestWithdrawalTxRemoveInput(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{1, 2}, []int64{})
|
tx := createWithdrawalTx(t, pool, []int64{1, 2}, []int64{})
|
||||||
inputs := tx.inputs
|
inputs := tx.inputs
|
||||||
// Make sure we have created the transaction with the expected inputs
|
// Make sure we have created the transaction with the expected inputs
|
||||||
checkTxInputs(t, tx, inputs)
|
checkTxInputs(t, tx, inputs)
|
||||||
|
@ -530,23 +529,23 @@ func TestWithdrawalTxRemoveInput(t *testing.T) {
|
||||||
gotRemovedInput := tx.removeInput()
|
gotRemovedInput := tx.removeInput()
|
||||||
|
|
||||||
// Check the popped input looks correct.
|
// 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)
|
t.Fatalf("Popped input wrong; got %v, want %v", gotRemovedInput, wantRemovedInput)
|
||||||
}
|
}
|
||||||
checkTxInputs(t, tx, inputs[0:1])
|
checkTxInputs(t, tx, inputs[0:1])
|
||||||
|
|
||||||
// Make sure that the remaining input is really the right one.
|
// 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)
|
t.Fatalf("Wrong input: got %v, want %v", tx.inputs[0], remainingInput)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithdrawalTxAddChange(t *testing.T) {
|
func TestWithdrawalTxAddChange(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
input, output, fee := int64(4e6), int64(3e6), int64(10)
|
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)))
|
restoreCalcTxFee := replaceCalculateTxFee(TstConstantFee(btcutil.Amount(fee)))
|
||||||
defer restoreCalcTxFee()
|
defer restoreCalcTxFee()
|
||||||
|
|
||||||
|
@ -569,11 +568,11 @@ func TestWithdrawalTxAddChange(t *testing.T) {
|
||||||
// add a change output when there's no satoshis left after paying all
|
// add a change output when there's no satoshis left after paying all
|
||||||
// outputs+fees.
|
// outputs+fees.
|
||||||
func TestWithdrawalTxAddChangeNoChange(t *testing.T) {
|
func TestWithdrawalTxAddChangeNoChange(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
input, output, fee := int64(4e6), int64(4e6), int64(0)
|
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)))
|
restoreCalcTxFee := replaceCalculateTxFee(TstConstantFee(btcutil.Amount(fee)))
|
||||||
defer restoreCalcTxFee()
|
defer restoreCalcTxFee()
|
||||||
|
|
||||||
|
@ -587,20 +586,20 @@ func TestWithdrawalTxAddChangeNoChange(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithdrawalTxToMsgTxNoInputsOrOutputsOrChange(t *testing.T) {
|
func TestWithdrawalTxToMsgTxNoInputsOrOutputsOrChange(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{}, []int64{})
|
tx := createWithdrawalTx(t, pool, []int64{}, []int64{})
|
||||||
msgtx := tx.toMsgTx()
|
msgtx := tx.toMsgTx()
|
||||||
compareMsgTxAndWithdrawalTxOutputs(t, msgtx, tx)
|
compareMsgTxAndWithdrawalTxOutputs(t, msgtx, tx)
|
||||||
compareMsgTxAndWithdrawalTxInputs(t, msgtx, tx)
|
compareMsgTxAndWithdrawalTxInputs(t, msgtx, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithdrawalTxToMsgTxNoInputsOrOutputsWithChange(t *testing.T) {
|
func TestWithdrawalTxToMsgTxNoInputsOrOutputsWithChange(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{}, []int64{})
|
tx := createWithdrawalTx(t, pool, []int64{}, []int64{})
|
||||||
tx.changeOutput = wire.NewTxOut(int64(1), []byte{})
|
tx.changeOutput = wire.NewTxOut(int64(1), []byte{})
|
||||||
|
|
||||||
msgtx := tx.toMsgTx()
|
msgtx := tx.toMsgTx()
|
||||||
|
@ -610,10 +609,10 @@ func TestWithdrawalTxToMsgTxNoInputsOrOutputsWithChange(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithdrawalTxToMsgTxWithInputButNoOutputsWithChange(t *testing.T) {
|
func TestWithdrawalTxToMsgTxWithInputButNoOutputsWithChange(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{1}, []int64{})
|
tx := createWithdrawalTx(t, pool, []int64{1}, []int64{})
|
||||||
tx.changeOutput = wire.NewTxOut(int64(1), []byte{})
|
tx.changeOutput = wire.NewTxOut(int64(1), []byte{})
|
||||||
|
|
||||||
msgtx := tx.toMsgTx()
|
msgtx := tx.toMsgTx()
|
||||||
|
@ -623,11 +622,11 @@ func TestWithdrawalTxToMsgTxWithInputButNoOutputsWithChange(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithdrawalTxToMsgTxWithInputOutputsAndChange(t *testing.T) {
|
func TestWithdrawalTxToMsgTxWithInputOutputsAndChange(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
|
|
||||||
defer tearDown()
|
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{})
|
tx.changeOutput = wire.NewTxOut(int64(7), []byte{})
|
||||||
|
|
||||||
msgtx := tx.toMsgTx()
|
msgtx := tx.toMsgTx()
|
||||||
|
@ -637,10 +636,10 @@ func TestWithdrawalTxToMsgTxWithInputOutputsAndChange(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithdrawalTxInputTotal(t *testing.T) {
|
func TestWithdrawalTxInputTotal(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{5}, []int64{})
|
tx := createWithdrawalTx(t, pool, []int64{5}, []int64{})
|
||||||
|
|
||||||
if tx.inputTotal() != btcutil.Amount(5) {
|
if tx.inputTotal() != btcutil.Amount(5) {
|
||||||
t.Fatalf("Wrong total output; got %v, want %v", tx.outputTotal(), 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) {
|
func TestWithdrawalTxOutputTotal(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{}, []int64{4})
|
tx := createWithdrawalTx(t, pool, []int64{}, []int64{4})
|
||||||
tx.changeOutput = wire.NewTxOut(int64(1), []byte{})
|
tx.changeOutput = wire.NewTxOut(int64(1), []byte{})
|
||||||
|
|
||||||
if tx.outputTotal() != btcutil.Amount(4) {
|
if tx.outputTotal() != btcutil.Amount(4) {
|
||||||
|
@ -660,12 +659,12 @@ func TestWithdrawalTxOutputTotal(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignMultiSigUTXO(t *testing.T) {
|
func TestSignMultiSigUTXO(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
// Create a new tx with a single input that we're going to sign.
|
// Create a new tx with a single input that we're going to sign.
|
||||||
mgr := pool.Manager()
|
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})
|
sigs, err := getRawSigs([]*withdrawalTx{tx})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -675,7 +674,7 @@ func TestSignMultiSigUTXO(t *testing.T) {
|
||||||
txSigs := sigs[tx.ntxid()]
|
txSigs := sigs[tx.ntxid()]
|
||||||
|
|
||||||
idx := 0 // The index of the tx input we're going to sign.
|
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() {
|
TstRunWithManagerUnlocked(t, mgr, func() {
|
||||||
if err = signMultiSigUTXO(mgr, msgtx, idx, pkScript, txSigs[idx]); err != nil {
|
if err = signMultiSigUTXO(mgr, msgtx, idx, pkScript, txSigs[idx]); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -684,11 +683,11 @@ func TestSignMultiSigUTXO(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignMultiSigUTXOUnparseablePkScript(t *testing.T) {
|
func TestSignMultiSigUTXOUnparseablePkScript(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
mgr := pool.Manager()
|
mgr := pool.Manager()
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{4e6}, []int64{})
|
tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{})
|
||||||
msgtx := tx.toMsgTx()
|
msgtx := tx.toMsgTx()
|
||||||
|
|
||||||
unparseablePkScript := []byte{0x01}
|
unparseablePkScript := []byte{0x01}
|
||||||
|
@ -698,11 +697,11 @@ func TestSignMultiSigUTXOUnparseablePkScript(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignMultiSigUTXOPkScriptNotP2SH(t *testing.T) {
|
func TestSignMultiSigUTXOPkScriptNotP2SH(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
mgr := pool.Manager()
|
mgr := pool.Manager()
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{4e6}, []int64{})
|
tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{})
|
||||||
addr, _ := btcutil.DecodeAddress("1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX", mgr.ChainParams())
|
addr, _ := btcutil.DecodeAddress("1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX", mgr.ChainParams())
|
||||||
pubKeyHashPkScript, _ := txscript.PayToAddrScript(addr.(*btcutil.AddressPubKeyHash))
|
pubKeyHashPkScript, _ := txscript.PayToAddrScript(addr.(*btcutil.AddressPubKeyHash))
|
||||||
msgtx := tx.toMsgTx()
|
msgtx := tx.toMsgTx()
|
||||||
|
@ -713,11 +712,11 @@ func TestSignMultiSigUTXOPkScriptNotP2SH(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignMultiSigUTXORedeemScriptNotFound(t *testing.T) {
|
func TestSignMultiSigUTXORedeemScriptNotFound(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
mgr := pool.Manager()
|
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
|
// This is a P2SH address for which the addr manager doesn't have the redeem
|
||||||
// script.
|
// script.
|
||||||
addr, _ := btcutil.DecodeAddress("3Hb4xcebcKg4DiETJfwjh8sF4uDw9rqtVC", mgr.ChainParams())
|
addr, _ := btcutil.DecodeAddress("3Hb4xcebcKg4DiETJfwjh8sF4uDw9rqtVC", mgr.ChainParams())
|
||||||
|
@ -733,11 +732,11 @@ func TestSignMultiSigUTXORedeemScriptNotFound(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignMultiSigUTXONotEnoughSigs(t *testing.T) {
|
func TestSignMultiSigUTXONotEnoughSigs(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
mgr := pool.Manager()
|
mgr := pool.Manager()
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{4e6}, []int64{})
|
tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{})
|
||||||
sigs, err := getRawSigs([]*withdrawalTx{tx})
|
sigs, err := getRawSigs([]*withdrawalTx{tx})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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.
|
idx := 0 // The index of the tx input we're going to sign.
|
||||||
// Here we provide reqSigs-1 signatures to SignMultiSigUTXO()
|
// 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]
|
txInSigs := txSigs[idx][:reqSigs-1]
|
||||||
pkScript := tx.inputs[idx].TxOut().PkScript
|
pkScript := tx.inputs[idx].PkScript
|
||||||
TstRunWithManagerUnlocked(t, mgr, func() {
|
TstRunWithManagerUnlocked(t, mgr, func() {
|
||||||
err = signMultiSigUTXO(mgr, msgtx, idx, pkScript, txInSigs)
|
err = signMultiSigUTXO(mgr, msgtx, idx, pkScript, txInSigs)
|
||||||
})
|
})
|
||||||
|
@ -758,15 +757,15 @@ func TestSignMultiSigUTXONotEnoughSigs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignMultiSigUTXOWrongRawSigs(t *testing.T) {
|
func TestSignMultiSigUTXOWrongRawSigs(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
mgr := pool.Manager()
|
mgr := pool.Manager()
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{4e6}, []int64{})
|
tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{})
|
||||||
sigs := []RawSig{RawSig{0x00}, RawSig{0x01}}
|
sigs := []RawSig{RawSig{0x00}, RawSig{0x01}}
|
||||||
|
|
||||||
idx := 0 // The index of the tx input we're going to sign.
|
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
|
var err error
|
||||||
TstRunWithManagerUnlocked(t, mgr, func() {
|
TstRunWithManagerUnlocked(t, mgr, func() {
|
||||||
err = signMultiSigUTXO(mgr, tx.toMsgTx(), idx, pkScript, sigs)
|
err = signMultiSigUTXO(mgr, tx.toMsgTx(), idx, pkScript, sigs)
|
||||||
|
@ -776,10 +775,10 @@ func TestSignMultiSigUTXOWrongRawSigs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetRawSigs(t *testing.T) {
|
func TestGetRawSigs(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{5e6, 4e6}, []int64{})
|
tx := createWithdrawalTx(t, pool, []int64{5e6, 4e6}, []int64{})
|
||||||
|
|
||||||
sigs, err := getRawSigs([]*withdrawalTx{tx})
|
sigs, err := getRawSigs([]*withdrawalTx{tx})
|
||||||
if err != nil {
|
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))
|
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
|
// 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
|
// 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) {
|
func TestGetRawSigsOnlyOnePrivKeyAvailable(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
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.
|
// 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:] {
|
for i := range series.privateKeys[1:] {
|
||||||
series.privateKeys[i] = nil
|
series.privateKeys[i] = nil
|
||||||
}
|
}
|
||||||
|
@ -824,13 +823,13 @@ func TestGetRawSigsOnlyOnePrivKeyAvailable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetRawSigsUnparseableRedeemScript(t *testing.T) {
|
func TestGetRawSigsUnparseableRedeemScript(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
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
|
// Change the redeem script for one of our tx inputs, to force an error in
|
||||||
// getRawSigs().
|
// getRawSigs().
|
||||||
tx.inputs[0].Address().script = []byte{0x01}
|
tx.inputs[0].addr.script = []byte{0x01}
|
||||||
|
|
||||||
_, err := getRawSigs([]*withdrawalTx{tx})
|
_, err := getRawSigs([]*withdrawalTx{tx})
|
||||||
|
|
||||||
|
@ -838,13 +837,13 @@ func TestGetRawSigsUnparseableRedeemScript(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetRawSigsInvalidAddrBranch(t *testing.T) {
|
func TestGetRawSigsInvalidAddrBranch(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
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
|
// Change the branch of our input's address to an invalid value, to force
|
||||||
// an error in getRawSigs().
|
// an error in getRawSigs().
|
||||||
tx.inputs[0].Address().branch = Branch(999)
|
tx.inputs[0].addr.branch = Branch(999)
|
||||||
|
|
||||||
_, err := getRawSigs([]*withdrawalTx{tx})
|
_, err := getRawSigs([]*withdrawalTx{tx})
|
||||||
|
|
||||||
|
@ -870,10 +869,10 @@ func TestOutBailmentIDSort(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxTooBig(t *testing.T) {
|
func TestTxTooBig(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
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 })
|
restoreCalcTxSize := replaceCalculateTxSize(func(tx *withdrawalTx) int { return txMaxSize - 1 })
|
||||||
if isTxTooBig(tx) {
|
if isTxTooBig(tx) {
|
||||||
|
@ -899,10 +898,10 @@ func TestTxTooBig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxSizeCalculation(t *testing.T) {
|
func TestTxSizeCalculation(t *testing.T) {
|
||||||
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
tearDown, pool, _ := TstCreatePoolAndTxStore(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
tx := createWithdrawalTx(t, pool, store, []int64{1, 5}, []int64{2})
|
tx := createWithdrawalTx(t, pool, []int64{1, 5}, []int64{2})
|
||||||
|
|
||||||
size := calculateTxSize(tx)
|
size := calculateTxSize(tx)
|
||||||
|
|
||||||
|
@ -912,7 +911,7 @@ func TestTxSizeCalculation(t *testing.T) {
|
||||||
// output.
|
// output.
|
||||||
restoreCalcTxFee := replaceCalculateTxFee(TstConstantFee(1))
|
restoreCalcTxFee := replaceCalculateTxFee(TstConstantFee(1))
|
||||||
defer restoreCalcTxFee()
|
defer restoreCalcTxFee()
|
||||||
seriesID := tx.inputs[0].Address().SeriesID()
|
seriesID := tx.inputs[0].addr.SeriesID()
|
||||||
tx.addChange(TstNewChangeAddress(t, pool, seriesID, 0).addr.ScriptAddress())
|
tx.addChange(TstNewChangeAddress(t, pool, seriesID, 0).addr.ScriptAddress())
|
||||||
msgtx := tx.toMsgTx()
|
msgtx := tx.toMsgTx()
|
||||||
sigs, err := getRawSigs([]*withdrawalTx{tx})
|
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
|
// 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
|
// bytes) so the estimate here can be up to 2 bytes bigger for every
|
||||||
// signature in every input's SigScript.
|
// 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
|
// 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*
|
// actual SignatureScript is at the upper boundary of one of the uint*
|
||||||
// types, and when that happens our dummy SignatureScript is likely to have
|
// 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
|
// checkNonEmptySigsForPrivKeys checks that every signature list in txSigs has
|
||||||
// one non-empty signature for every non-nil private key in the given list. This
|
// 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
|
// 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.
|
// 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) {
|
if len(tx.inputs) != len(inputs) {
|
||||||
t.Fatalf("Wrong number of inputs in tx; got %d, want %d", 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 {
|
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])
|
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
|
// transaction (using the given raw signatures and the pkScripts from credits) and execute
|
||||||
// those scripts to validate them.
|
// those scripts to validate them.
|
||||||
func signTxAndValidate(t *testing.T, mgr *waddrmgr.Manager, tx *wire.MsgTx, txSigs TxSigs,
|
func signTxAndValidate(t *testing.T, mgr *waddrmgr.Manager, tx *wire.MsgTx, txSigs TxSigs,
|
||||||
credits []Credit) {
|
credits []credit) {
|
||||||
for i := range tx.TxIn {
|
for i := range tx.TxIn {
|
||||||
pkScript := credits[i].TxOut().PkScript
|
pkScript := credits[i].PkScript
|
||||||
TstRunWithManagerUnlocked(t, mgr, func() {
|
TstRunWithManagerUnlocked(t, mgr, func() {
|
||||||
if err := signMultiSigUTXO(mgr, tx, i, pkScript, txSigs[i]); err != nil {
|
if err := signMultiSigUTXO(mgr, tx, i, pkScript, txSigs[i]); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -1076,9 +1064,9 @@ func compareMsgTxAndWithdrawalTxInputs(t *testing.T, msgtx *wire.MsgTx, tx *with
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, txin := range msgtx.TxIn {
|
for i, txin := range msgtx.TxIn {
|
||||||
outpoint := tx.inputs[i].OutPoint()
|
outpoint := tx.inputs[i].OutPoint
|
||||||
if txin.PreviousOutPoint != *outpoint {
|
if txin.PreviousOutPoint != outpoint {
|
||||||
t.Fatalf("Wrong outpoint; got %v expected %v", txin.PreviousOutPoint, *outpoint)
|
t.Fatalf("Wrong outpoint; got %v expected %v", txin.PreviousOutPoint, outpoint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue