Pass txstore.Credit/Debits directly, not pointers.

The Credit and Debits structures are simple wrappers around an
embedded *txstore.TxRecord, as well as an output index in the case of
Credit.  This means that a Credit is at most two words, while a Debits
struct is just one.  To avoid the unnecessary garbage of creating
Credit and Debits structures on the heap (where the underlying
TxRecord likely already is), simply pass around everywhere as
non-pointer types, and modify the receivers for all Credit and Debits
methods to non-pointer receivers since none of them ever modify the
value.
This commit is contained in:
Josh Rickmar 2014-06-18 00:16:08 -05:00
parent 9153a342e4
commit 6a72a0ad4d
6 changed files with 57 additions and 58 deletions

View file

@ -651,7 +651,7 @@ func (a *Account) RecoverAddresses(n int) error {
// ReqSpentUtxoNtfns sends a message to btcd to request updates for when // ReqSpentUtxoNtfns sends a message to btcd to request updates for when
// a stored UTXO has been spent. // a stored UTXO has been spent.
func ReqSpentUtxoNtfns(credits []*txstore.Credit) { func ReqSpentUtxoNtfns(credits []txstore.Credit) {
ops := make([]*btcwire.OutPoint, 0, len(credits)) ops := make([]*btcwire.OutPoint, 0, len(credits))
for _, c := range credits { for _, c := range credits {
op := c.OutPoint() op := c.OutPoint()

View file

@ -61,13 +61,13 @@ var TxFeeIncrement = struct {
type CreatedTx struct { type CreatedTx struct {
tx *btcutil.Tx tx *btcutil.Tx
inputs []*txstore.Credit inputs []txstore.Credit
changeAddr btcutil.Address changeAddr btcutil.Address
} }
// ByAmount defines the methods needed to satisify sort.Interface to // ByAmount defines the methods needed to satisify sort.Interface to
// sort a slice of Utxos by their amount. // sort a slice of Utxos by their amount.
type ByAmount []*txstore.Credit type ByAmount []txstore.Credit
func (u ByAmount) Len() int { return len(u) } func (u ByAmount) Len() int { return len(u) }
func (u ByAmount) Less(i, j int) bool { return u[i].Amount() < u[j].Amount() } func (u ByAmount) Less(i, j int) bool { return u[i].Amount() < u[j].Amount() }
@ -79,13 +79,13 @@ func (u ByAmount) Swap(i, j int) { u[i], u[j] = u[j], u[i] }
// combination of all selected previous outputs. err will equal // combination of all selected previous outputs. err will equal
// ErrInsufficientFunds if there are not enough unspent outputs to spend amt // ErrInsufficientFunds if there are not enough unspent outputs to spend amt
// amt. // amt.
func selectInputs(eligible []*txstore.Credit, amt btcutil.Amount, func selectInputs(eligible []txstore.Credit, amt btcutil.Amount,
minconf int) (selected []*txstore.Credit, out btcutil.Amount, err error) { minconf int) (selected []txstore.Credit, out btcutil.Amount, err error) {
// Iterate throguh eligible transactions, appending to outputs and // Iterate throguh eligible transactions, appending to outputs and
// increasing out. This is finished when out is greater than the // increasing out. This is finished when out is greater than the
// requested amt to spend. // requested amt to spend.
selected = make([]*txstore.Credit, 0, len(eligible)) selected = make([]txstore.Credit, 0, len(eligible))
for _, e := range eligible { for _, e := range eligible {
selected = append(selected, e) selected = append(selected, e)
out += e.Amount() out += e.Amount()
@ -166,7 +166,7 @@ func (a *Account) txToPairs(pairs map[string]btcutil.Amount,
// time) are not P2PKH outputs. Other inputs must be manually included // time) are not P2PKH outputs. Other inputs must be manually included
// in transactions and sent (for example, using createrawtransaction, // in transactions and sent (for example, using createrawtransaction,
// signrawtransaction, and sendrawtransaction). // signrawtransaction, and sendrawtransaction).
eligible := make([]*txstore.Credit, 0, len(unspent)) eligible := make([]txstore.Credit, 0, len(unspent))
for i := range unspent { for i := range unspent {
switch btcscript.GetScriptClass(unspent[i].TxOut().PkScript) { switch btcscript.GetScriptClass(unspent[i].TxOut().PkScript) {
case btcscript.PubKeyHashTy: case btcscript.PubKeyHashTy:
@ -189,7 +189,7 @@ func (a *Account) txToPairs(pairs map[string]btcutil.Amount,
// by amount in reverse order. // by amount in reverse order.
sort.Sort(sort.Reverse(ByAmount(eligible))) sort.Sort(sort.Reverse(ByAmount(eligible)))
var selectedInputs []*txstore.Credit var selectedInputs []txstore.Credit
// changeAddr is 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. // again in case a change utxo has already been chosen.
var changeAddr btcutil.Address var changeAddr btcutil.Address
@ -355,7 +355,7 @@ func minimumFee(tx *btcwire.MsgTx, allowFree bool) btcutil.Amount {
// allowFree calculates the transaction priority and checks that the // allowFree calculates the transaction priority and checks that the
// priority reaches a certain threshhold. If the threshhold is // priority reaches a certain threshhold. If the threshhold is
// reached, a free transaction fee is allowed. // reached, a free transaction fee is allowed.
func allowFree(curHeight int32, txouts []*txstore.Credit, txSize int) bool { func allowFree(curHeight int32, txouts []txstore.Credit, txSize int) bool {
const blocksPerDayEstimate = 144 const blocksPerDayEstimate = 144
const txSizeEstimate = 250 const txSizeEstimate = 250

View file

@ -1477,7 +1477,7 @@ func GetTransaction(icmd btcjson.Cmd) (interface{}, error) {
} }
received := btcutil.Amount(0) received := btcutil.Amount(0)
var debitTx *txstore.TxRecord var debits *txstore.Debits
var debitAccount string var debitAccount string
var targetAddr string var targetAddr string
@ -1516,17 +1516,16 @@ func GetTransaction(icmd btcjson.Cmd) (interface{}, error) {
}) })
} }
if e.Tx.Debits() != nil { if d, err := e.Tx.Debits(); err == nil {
// There should only be a single debits record for any // There should only be a single debits record for any
// of the account's transaction records. // of the account's transaction records.
debitTx = e.Tx debits = &d
debitAccount = e.Account debitAccount = e.Account
} }
} }
totalAmount := received totalAmount := received
if debitTx != nil { if debits != nil {
debits := debitTx.Debits()
totalAmount -= debits.InputAmount() totalAmount -= debits.InputAmount()
info := btcjson.GetTransactionDetailsResult{ info := btcjson.GetTransactionDetailsResult{
Account: debitAccount, Account: debitAccount,

View file

@ -90,7 +90,7 @@
// } // }
// //
// // Mark r2 as debiting from record 1's 0th credit. // // Mark r2 as debiting from record 1's 0th credit.
// d2, err := r2.AddDebits([]*txstore.Credit{c1o0}) // d2, err := r2.AddDebits([]txstore.Credit{c1o0})
// if err != nil { // if err != nil {
// // handle error // // handle error
// } // }

View file

@ -30,7 +30,7 @@ func (t *TxRecord) ToJSON(account string, chainHeight int32,
net *btcnet.Params) ([]btcjson.ListTransactionsResult, error) { net *btcnet.Params) ([]btcjson.ListTransactionsResult, error) {
results := []btcjson.ListTransactionsResult{} results := []btcjson.ListTransactionsResult{}
if d := t.Debits(); d != nil { if d, err := t.Debits(); err == nil {
r, err := d.ToJSON(account, chainHeight, net) r, err := d.ToJSON(account, chainHeight, net)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -613,21 +613,21 @@ func (t *TxRecord) Block() (*Block, error) {
// AddDebits marks a transaction record as having debited from all them transaction // AddDebits marks a transaction record as having debited from all them transaction
// credits in the spent slice. If spent is nil, the previous debits will be found, // credits in the spent slice. If spent is nil, the previous debits will be found,
// however this is an expensive lookup and should be avoided if possible. // however this is an expensive lookup and should be avoided if possible.
func (t *TxRecord) AddDebits(spent []*Credit) (*Debits, error) { func (t *TxRecord) AddDebits(spent []Credit) (Debits, error) {
if t.debits == nil { if t.debits == nil {
// Find now-spent credits if no debits have been previously set // Find now-spent credits if no debits have been previously set
// and none were passed in by the caller. // and none were passed in by the caller.
if len(spent) == 0 { if len(spent) == 0 {
foundSpent, err := t.s.findPreviousCredits(t.Tx()) foundSpent, err := t.s.findPreviousCredits(t.Tx())
if err != nil { if err != nil {
return nil, err return Debits{}, err
} }
spent = foundSpent spent = foundSpent
} }
debitAmount, err := t.s.markOutputsSpent(spent, t) debitAmount, err := t.s.markOutputsSpent(spent, t)
if err != nil { if err != nil {
return nil, err return Debits{}, err
} }
t.debits = &debits{amount: debitAmount} t.debits = &debits{amount: debitAmount}
} }
@ -655,20 +655,20 @@ func (t *TxRecord) AddDebits(spent []*Credit) (*Debits, error) {
err := t.txRecord.setDebitsSpends(prevOutputKeys, t.tx) err := t.txRecord.setDebitsSpends(prevOutputKeys, t.tx)
if err != nil { if err != nil {
if err == ErrDuplicateInsert { if err == ErrDuplicateInsert {
return &Debits{t}, nil return Debits{t}, nil
} }
return nil, err return Debits{}, err
} }
} }
} }
return &Debits{t}, nil return Debits{t}, nil
} }
// findPreviousCredits searches for all unspent credits that make up the inputs // findPreviousCredits searches for all unspent credits that make up the inputs
// for tx. // for tx.
func (s *Store) findPreviousCredits(tx *btcutil.Tx) ([]*Credit, error) { func (s *Store) findPreviousCredits(tx *btcutil.Tx) ([]Credit, error) {
type createdCredit struct { type createdCredit struct {
credit *Credit credit Credit
err error err error
} }
@ -688,11 +688,11 @@ func (s *Store) findPreviousCredits(tx *btcutil.Tx) ([]*Credit, error) {
return return
} }
t := &TxRecord{key, r, s} t := &TxRecord{key, r, s}
c := &Credit{t, op.Index} c := Credit{t, op.Index}
creditChans[i] <- createdCredit{credit: c} creditChans[i] <- createdCredit{credit: c}
}(i, txIn.PreviousOutpoint) }(i, txIn.PreviousOutpoint)
} }
spent := make([]*Credit, 0, len(inputs)) spent := make([]Credit, 0, len(inputs))
for _, c := range creditChans { for _, c := range creditChans {
cc, ok := <-c cc, ok := <-c
if !ok { if !ok {
@ -708,7 +708,7 @@ func (s *Store) findPreviousCredits(tx *btcutil.Tx) ([]*Credit, error) {
// markOutputsSpent marks each previous credit spent by t as spent. The total // markOutputsSpent marks each previous credit spent by t as spent. The total
// input of all spent previous outputs is returned. // input of all spent previous outputs is returned.
func (s *Store) markOutputsSpent(spent []*Credit, t *TxRecord) (btcutil.Amount, error) { func (s *Store) markOutputsSpent(spent []Credit, t *TxRecord) (btcutil.Amount, error) {
var a btcutil.Amount var a btcutil.Amount
for _, prev := range spent { for _, prev := range spent {
op := prev.OutPoint() op := prev.OutPoint()
@ -755,17 +755,17 @@ func (s *Store) markOutputsSpent(spent []*Credit, t *TxRecord) (btcutil.Amount,
// AddCredit marks the transaction record as containing a transaction output // AddCredit marks the transaction record as containing a transaction output
// spendable by wallet. The output is added unspent, and is marked spent // spendable by wallet. The output is added unspent, and is marked spent
// when a new transaction spending the output is inserted into the store. // when a new transaction spending the output is inserted into the store.
func (t *TxRecord) AddCredit(index uint32, change bool) (*Credit, error) { func (t *TxRecord) AddCredit(index uint32, change bool) (Credit, error) {
if len(t.tx.MsgTx().TxOut) <= int(index) { if len(t.tx.MsgTx().TxOut) <= int(index) {
return nil, errors.New("transaction output does not exist") return Credit{}, errors.New("transaction output does not exist")
} }
c := &credit{change: change} c := &credit{change: change}
if err := t.txRecord.setCredit(c, index, t.tx); err != nil { if err := t.txRecord.setCredit(c, index, t.tx); err != nil {
if err == ErrDuplicateInsert { if err == ErrDuplicateInsert {
return &Credit{t, index}, nil return Credit{t, index}, nil
} }
return nil, err return Credit{}, err
} }
switch t.BlockHeight { switch t.BlockHeight {
@ -773,7 +773,7 @@ func (t *TxRecord) AddCredit(index uint32, change bool) (*Credit, error) {
default: default:
b, err := t.s.lookupBlock(t.BlockHeight) b, err := t.s.lookupBlock(t.BlockHeight)
if err != nil { if err != nil {
return nil, err return Credit{}, err
} }
// New outputs are added unspent. // New outputs are added unspent.
@ -787,7 +787,7 @@ func (t *TxRecord) AddCredit(index uint32, change bool) (*Credit, error) {
} }
} }
return &Credit{t, index}, nil return Credit{t, index}, nil
} }
// Rollback removes all blocks at height onwards, moving any transactions within // Rollback removes all blocks at height onwards, moving any transactions within
@ -1024,9 +1024,9 @@ func (s *Store) removeConflict(r *txRecord) error {
// UnspentOutputs returns all unspent received transaction outputs. // UnspentOutputs returns all unspent received transaction outputs.
// The order is undefined. // The order is undefined.
func (s *Store) UnspentOutputs() ([]*Credit, error) { func (s *Store) UnspentOutputs() ([]Credit, error) {
type createdCredit struct { type createdCredit struct {
credit *Credit credit Credit
err error err error
} }
@ -1041,13 +1041,13 @@ func (s *Store) UnspentOutputs() ([]*Credit, error) {
return return
} }
t := &TxRecord{key, r, s} t := &TxRecord{key, r, s}
c := &Credit{t, opIndex} c := Credit{t, opIndex}
creditChans[i] <- createdCredit{credit: c} creditChans[i] <- createdCredit{credit: c}
}(i, op.Index) }(i, op.Index)
i++ i++
} }
unspent := make([]*Credit, 0, len(s.unspent)) unspent := make([]Credit, 0, len(s.unspent))
for _, c := range creditChans { for _, c := range creditChans {
cc, ok := <-c cc, ok := <-c
if !ok { if !ok {
@ -1066,7 +1066,7 @@ func (s *Store) UnspentOutputs() ([]*Credit, error) {
} }
key := BlockTxKey{BlockHeight: -1} key := BlockTxKey{BlockHeight: -1}
txRecord := &TxRecord{key, r, s} txRecord := &TxRecord{key, r, s}
c := &Credit{txRecord, uint32(outputIndex)} c := Credit{txRecord, uint32(outputIndex)}
unspent = append(unspent, c) unspent = append(unspent, c)
} }
} }
@ -1082,7 +1082,7 @@ type unspentTx struct {
sliceIndex uint32 sliceIndex uint32
} }
type creditSlice []*Credit type creditSlice []Credit
func (s creditSlice) Len() int { func (s creditSlice) Len() int {
return len(s) return len(s)
@ -1124,10 +1124,10 @@ func (s creditSlice) Swap(i, j int) {
// in the same block (same number of confirmations) are sorted by block // in the same block (same number of confirmations) are sorted by block
// index in increasing order. Credits (outputs) from the same transaction // index in increasing order. Credits (outputs) from the same transaction
// are sorted by output index in increasing order. // are sorted by output index in increasing order.
func (s *Store) SortedUnspentOutputs() ([]*Credit, error) { func (s *Store) SortedUnspentOutputs() ([]Credit, error) {
unspent, err := s.UnspentOutputs() unspent, err := s.UnspentOutputs()
if err != nil { if err != nil {
return []*Credit{}, err return []Credit{}, err
} }
sort.Sort(sort.Reverse(creditSlice(unspent))) sort.Sort(sort.Reverse(creditSlice(unspent)))
return unspent, nil return unspent, nil
@ -1258,29 +1258,29 @@ func (r byReceiveDate) Len() int { return len(r) }
func (r byReceiveDate) Less(i, j int) bool { return r[i].received.Before(r[j].received) } func (r byReceiveDate) Less(i, j int) bool { return r[i].received.Before(r[j].received) }
func (r byReceiveDate) Swap(i, j int) { r[i], r[j] = r[j], r[i] } func (r byReceiveDate) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
// Debits returns the debit record for the transaction, or nil if the // Debits returns the debit record for the transaction, or a non-nil error if
// transaction does not debit from any previous transaction credits. // the transaction does not debit from any previous transaction credits.
func (t *TxRecord) Debits() *Debits { func (t *TxRecord) Debits() (Debits, error) {
if t.debits == nil { if t.debits == nil {
return nil return Debits{}, errors.New("no debits")
} }
return &Debits{t} return Debits{t}, nil
} }
// Credits returns all credit records for this transaction's outputs that are or // Credits returns all credit records for this transaction's outputs that are or
// were spendable by wallet. // were spendable by wallet.
func (t *TxRecord) Credits() []*Credit { func (t *TxRecord) Credits() []Credit {
credits := make([]*Credit, 0, len(t.credits)) credits := make([]Credit, 0, len(t.credits))
for i, c := range t.credits { for i, c := range t.credits {
if c != nil { if c != nil {
credits = append(credits, &Credit{t, uint32(i)}) credits = append(credits, Credit{t, uint32(i)})
} }
} }
return credits return credits
} }
// InputAmount returns the total amount debited from previous credits. // InputAmount returns the total amount debited from previous credits.
func (d *Debits) InputAmount() btcutil.Amount { func (d Debits) InputAmount() btcutil.Amount {
return d.txRecord.debits.amount return d.txRecord.debits.amount
} }
@ -1301,13 +1301,13 @@ func (t *TxRecord) OutputAmount(ignoreChange bool) btcutil.Amount {
// Fee returns the difference between the debited amount and the total // Fee returns the difference between the debited amount and the total
// transaction output. // transaction output.
func (d *Debits) Fee() btcutil.Amount { func (d Debits) Fee() btcutil.Amount {
return d.InputAmount() - d.OutputAmount(false) return d.InputAmount() - d.OutputAmount(false)
} }
// Addresses parses the pubkey script, extracting all addresses for a // Addresses parses the pubkey script, extracting all addresses for a
// standard script. // standard script.
func (c *Credit) Addresses(net *btcnet.Params) (btcscript.ScriptClass, func (c Credit) Addresses(net *btcnet.Params) (btcscript.ScriptClass,
[]btcutil.Address, int, error) { []btcutil.Address, int, error) {
msgTx := c.Tx().MsgTx() msgTx := c.Tx().MsgTx()
@ -1316,7 +1316,7 @@ func (c *Credit) Addresses(net *btcnet.Params) (btcscript.ScriptClass,
} }
// Change returns whether the credit is the result of a change output. // Change returns whether the credit is the result of a change output.
func (c *Credit) Change() bool { func (c Credit) Change() bool {
return c.txRecord.credits[c.OutputIndex].change return c.txRecord.credits[c.OutputIndex].change
} }
@ -1338,19 +1338,19 @@ func (t *TxRecord) IsCoinbase() bool {
} }
// Amount returns the amount credited to the account from a transaction output. // Amount returns the amount credited to the account from a transaction output.
func (c *Credit) Amount() btcutil.Amount { func (c Credit) Amount() btcutil.Amount {
msgTx := c.Tx().MsgTx() msgTx := c.Tx().MsgTx()
return btcutil.Amount(msgTx.TxOut[c.OutputIndex].Value) return btcutil.Amount(msgTx.TxOut[c.OutputIndex].Value)
} }
// OutPoint returns the outpoint needed to include in a transaction input // OutPoint returns the outpoint needed to include in a transaction input
// to spend this output. // to spend this output.
func (c *Credit) OutPoint() *btcwire.OutPoint { func (c Credit) OutPoint() *btcwire.OutPoint {
return btcwire.NewOutPoint(c.Tx().Sha(), c.OutputIndex) return btcwire.NewOutPoint(c.Tx().Sha(), c.OutputIndex)
} }
// outputKey creates and returns the block lookup key for this credit. // outputKey creates and returns the block lookup key for this credit.
func (c *Credit) outputKey() *BlockOutputKey { func (c Credit) outputKey() *BlockOutputKey {
return &BlockOutputKey{ return &BlockOutputKey{
BlockTxKey: c.BlockTxKey, BlockTxKey: c.BlockTxKey,
OutputIndex: c.OutputIndex, OutputIndex: c.OutputIndex,
@ -1358,12 +1358,12 @@ func (c *Credit) outputKey() *BlockOutputKey {
} }
// Spent returns whether the transaction output is currently spent or not. // Spent returns whether the transaction output is currently spent or not.
func (c *Credit) Spent() bool { func (c Credit) Spent() bool {
return c.txRecord.credits[c.OutputIndex].spentBy != nil return c.txRecord.credits[c.OutputIndex].spentBy != nil
} }
// TxOut returns the transaction output which this credit references. // TxOut returns the transaction output which this credit references.
func (c *Credit) TxOut() *btcwire.TxOut { func (c Credit) TxOut() *btcwire.TxOut {
return c.Tx().MsgTx().TxOut[c.OutputIndex] return c.Tx().MsgTx().TxOut[c.OutputIndex]
} }