Fix listtransactions category for coinbase outputs.

The category for a received coinbase output should be "generate" for a
mature coinbase (one that has reached btcchain.CoinbaseMaturity
confirmations), or "immature" if the required number of confirmations
has not been reached yet.  New Confirmed and Confirmations methods
have been added to the transaction store's TxRecord type to check if
the required number of confirmations have been met for coinbase
outputs.

While here, update the main package to use the new TxRecord methods,
rather than duplicating the confirmation checking code in two places.
This commit is contained in:
Josh Rickmar 2014-05-06 22:48:12 -05:00
parent 17ebf9461f
commit e39fa32487
6 changed files with 42 additions and 36 deletions

View file

@ -142,7 +142,7 @@ func (a *Account) CalculateAddressBalance(addr btcutil.Address, confirms int) fl
return 0.
}
for _, credit := range unspent {
if confirmed(confirms, credit.BlockHeight, bs.Height) {
if credit.Confirmed(confirms, bs.Height) {
// We only care about the case where len(addrs) == 1, and err
// will never be non-nil in that case
_, addrs, _, _ := credit.Addresses(cfg.Net())
@ -188,7 +188,7 @@ func (a *Account) ListSinceBlock(since, curBlockHeight int32,
// Transactions that have not met minconf confirmations are to
// be ignored.
if !confirmed(minconf, txRecord.BlockHeight, curBlockHeight) {
if !txRecord.Confirmed(minconf, curBlockHeight) {
continue
}
@ -681,7 +681,7 @@ func (a *Account) TotalReceived(confirms int) (float64, error) {
}
// Tally if the appropiate number of block confirmations have passed.
if confirmed(confirms, c.BlockHeight, bs.Height) {
if c.Confirmed(confirms, bs.Height) {
amount += c.Amount()
}
}
@ -689,21 +689,3 @@ func (a *Account) TotalReceived(confirms int) (float64, error) {
return amount.ToUnit(btcutil.AmountBTC), nil
}
// confirmed checks whether a transaction at height txHeight has met
// minconf confirmations for a blockchain at height curHeight.
func confirmed(minconf int, txHeight, curHeight int32) bool {
return confirms(txHeight, curHeight) >= int32(minconf)
}
// 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
}
}

View file

@ -810,9 +810,8 @@ func (am *AccountManager) ListUnspent(minconf, maxconf int,
return nil, err
}
for _, credit := range unspent {
confs := confirms(credit.BlockHeight, bs.Height)
switch {
case int(confs) < minconf, int(confs) > maxconf:
confs := credit.Confirmations(bs.Height)
if int(confs) < minconf || int(confs) > maxconf {
continue
}

View file

@ -89,7 +89,7 @@ func (u ByAmount) Swap(i, j int) {
// 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(utxos []*tx.Credit, amt btcutil.Amount,
func selectInputs(credits []*tx.Credit, amt btcutil.Amount,
minconf int) (selected []*tx.Credit, out btcutil.Amount, err error) {
bs, err := GetCurBlock()
@ -100,18 +100,18 @@ func selectInputs(utxos []*tx.Credit, amt btcutil.Amount,
// 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([]*tx.Credit, 0, len(utxos))
for _, utxo := range utxos {
if confirmed(minconf, utxo.BlockHeight, bs.Height) {
eligible := make([]*tx.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 utxo.IsCoinbase() {
confs := confirms(utxo.BlockHeight, bs.Height)
if confs < btcchain.CoinbaseMaturity {
if c.IsCoinbase() {
target := btcchain.CoinbaseMaturity
if !c.Confirmed(target, bs.Height) {
continue
}
}
eligible = append(eligible, utxo)
eligible = append(eligible, c)
}
}
sort.Sort(sort.Reverse(ByAmount(eligible)))

View file

@ -1057,7 +1057,7 @@ func GetTransaction(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
ret.BlockIndex = int64(first.Tx.Tx().Index())
ret.BlockHash = txBlock.Hash.String()
ret.BlockTime = txBlock.Time.Unix()
ret.Confirmations = int64(confirms(txr.BlockHeight, bs.Height))
ret.Confirmations = int64(txr.Confirmations(bs.Height))
}
// TODO(oga) if the tx is a coinbase we should set "generated" to true.
// Since we do not mine this currently is never the case.

View file

@ -17,6 +17,7 @@
package tx
import (
"github.com/conformal/btcchain"
"github.com/conformal/btcjson"
"github.com/conformal/btcscript"
"github.com/conformal/btcutil"
@ -81,7 +82,7 @@ func (d *Debits) ToJSON(account string, chainHeight int32,
result.BlockHash = b.Hash.String()
result.BlockIndex = int64(d.Tx().Index())
result.BlockTime = b.Time.Unix()
result.Confirmations = int64(confirms(b.Height, chainHeight))
result.Confirmations = int64(d.Confirmations(chainHeight))
}
reply = append(reply, result)
}
@ -103,9 +104,21 @@ func (c *Credit) ToJSON(account string, chainHeight int32,
address = addrs[0].EncodeAddress()
}
var category string
switch {
case c.IsCoinbase():
if c.Confirmed(btcchain.CoinbaseMaturity, chainHeight) {
category = "generate"
} else {
category = "immature"
}
default:
category = "receive"
}
result := btcjson.ListTransactionsResult{
Account: account,
Category: "receive",
Category: category,
Address: address,
Amount: btcutil.Amount(txout.Value).ToUnit(btcutil.AmountBTC),
TxID: c.Tx().Sha().String(),
@ -122,7 +135,7 @@ func (c *Credit) ToJSON(account string, chainHeight int32,
result.BlockHash = b.Hash.String()
result.BlockIndex = int64(c.Tx().Index())
result.BlockTime = b.Time.Unix()
result.Confirmations = int64(confirms(b.Height, chainHeight))
result.Confirmations = int64(c.Confirmations(chainHeight))
}
return result, nil

View file

@ -1297,6 +1297,18 @@ func (c *Credit) Change() bool {
return c.txRecord.credits[c.OutputIndex].change
}
// Confirmed returns whether a transaction has reached some target number of
// confirmations, given the current best chain height.
func (t *TxRecord) Confirmed(target int, chainHeight int32) bool {
return confirmed(target, t.BlockHeight, chainHeight)
}
// Confirmations returns the total number of confirmations a transaction has
// reached, given the current best chain height.
func (t *TxRecord) Confirmations(chainHeight int32) int32 {
return confirms(t.BlockHeight, chainHeight)
}
// IsCoinbase returns whether the transaction is a coinbase.
func (t *TxRecord) IsCoinbase() bool {
return t.BlockHeight != -1 && t.BlockIndex == 0