Improve tx creation performance.

Unspent inputs were being sorted multiple times in an inner loop.
This change moves the sort outside the loop so it is only performed
once.  While here, also move the transaction confirmation filter
outside the inner loop, as these can be calculated just once.
This commit is contained in:
Josh Rickmar 2014-06-13 14:52:49 -05:00
parent c0e990fb3f
commit 83b70e6c7e

View file

@ -75,40 +75,17 @@ func (u ByAmount) Swap(i, j int) { u[i], u[j] = u[j], u[i] }
// selectInputs selects the minimum number possible of unspent
// outputs to use to create a new transaction that spends amt satoshis.
// Previous outputs with less than minconf confirmations are ignored. btcout
// is the total number of satoshis which would be spent by the combination
// of all selected previous outputs. err will equal ErrInsufficientFunds if there
// are not enough unspent outputs to spend amt.
func selectInputs(credits []*txstore.Credit, amt btcutil.Amount,
// btcout is the total number of satoshis which would be spent by the
// combination of all selected previous outputs. err will equal
// ErrInsufficientFunds if there are not enough unspent outputs to spend amt
// amt.
func selectInputs(eligible []*txstore.Credit, amt btcutil.Amount,
minconf int) (selected []*txstore.Credit, out btcutil.Amount, err error) {
bs, err := GetCurBlock()
if err != nil {
return nil, 0, err
}
// Create list of eligible unspent previous outputs to use as tx
// inputs, and sort by the amount in reverse order so a minimum number
// of inputs is needed.
eligible := make([]*txstore.Credit, 0, len(credits))
for _, c := range credits {
if c.Confirmed(minconf, bs.Height) {
// Coinbase transactions must have have reached maturity
// before their outputs may be spent.
if c.IsCoinbase() {
target := btcchain.CoinbaseMaturity
if !c.Confirmed(target, bs.Height) {
continue
}
}
eligible = append(eligible, c)
}
}
sort.Sort(sort.Reverse(ByAmount(eligible)))
// Iterate throguh eligible transactions, appending to outputs and
// increasing out. This is finished when out is greater than the
// requested amt to spend.
selected = make([]*txstore.Credit, 0, len(eligible))
for _, e := range eligible {
selected = append(selected, e)
out += e.Amount()
@ -184,6 +161,7 @@ func (a *Account) txToPairs(pairs map[string]btcutil.Amount,
if err != nil {
return nil, err
}
// Filter out unspendable outputs, that is, remove those that (at this
// time) are not P2PKH outputs. Other inputs must be manually included
// in transactions and sent (for example, using createrawtransaction,
@ -192,12 +170,27 @@ func (a *Account) txToPairs(pairs map[string]btcutil.Amount,
for i := range unspent {
switch btcscript.GetScriptClass(unspent[i].TxOut().PkScript) {
case btcscript.PubKeyHashTy:
if !unspent[i].Confirmed(minconf, bs.Height) {
continue
}
// Coinbase transactions must have have reached maturity
// before their outputs may be spent.
if unspent[i].IsCoinbase() {
target := btcchain.CoinbaseMaturity
if !unspent[i].Confirmed(target, bs.Height) {
continue
}
}
eligible = append(eligible, unspent[i])
}
}
// Sort eligible inputs, as selectInputs expects these to be sorted
// by amount in reverse order.
sort.Sort(sort.Reverse(ByAmount(eligible)))
var selectedInputs []*txstore.Credit
// These are nil/zeroed until a change address is needed, and reused
// changeAddr is nil/zeroed until a change address is needed, and reused
// again in case a change utxo has already been chosen.
var changeAddr btcutil.Address