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:
parent
17ebf9461f
commit
e39fa32487
6 changed files with 42 additions and 36 deletions
24
account.go
24
account.go
|
@ -142,7 +142,7 @@ func (a *Account) CalculateAddressBalance(addr btcutil.Address, confirms int) fl
|
||||||
return 0.
|
return 0.
|
||||||
}
|
}
|
||||||
for _, credit := range unspent {
|
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
|
// We only care about the case where len(addrs) == 1, and err
|
||||||
// will never be non-nil in that case
|
// will never be non-nil in that case
|
||||||
_, addrs, _, _ := credit.Addresses(cfg.Net())
|
_, 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
|
// Transactions that have not met minconf confirmations are to
|
||||||
// be ignored.
|
// be ignored.
|
||||||
if !confirmed(minconf, txRecord.BlockHeight, curBlockHeight) {
|
if !txRecord.Confirmed(minconf, curBlockHeight) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -681,7 +681,7 @@ func (a *Account) TotalReceived(confirms int) (float64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tally if the appropiate number of block confirmations have passed.
|
// 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()
|
amount += c.Amount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -689,21 +689,3 @@ func (a *Account) TotalReceived(confirms int) (float64, error) {
|
||||||
|
|
||||||
return amount.ToUnit(btcutil.AmountBTC), nil
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -810,9 +810,8 @@ func (am *AccountManager) ListUnspent(minconf, maxconf int,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, credit := range unspent {
|
for _, credit := range unspent {
|
||||||
confs := confirms(credit.BlockHeight, bs.Height)
|
confs := credit.Confirmations(bs.Height)
|
||||||
switch {
|
if int(confs) < minconf || int(confs) > maxconf {
|
||||||
case int(confs) < minconf, int(confs) > maxconf:
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
createtx.go
16
createtx.go
|
@ -89,7 +89,7 @@ func (u ByAmount) Swap(i, j int) {
|
||||||
// is the total number of satoshis which would be spent by the combination
|
// is the total number of satoshis which would be spent by the combination
|
||||||
// of all selected previous outputs. err will equal ErrInsufficientFunds if there
|
// of all selected previous outputs. err will equal ErrInsufficientFunds if there
|
||||||
// are not enough unspent outputs to spend amt.
|
// 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) {
|
minconf int) (selected []*tx.Credit, out btcutil.Amount, err error) {
|
||||||
|
|
||||||
bs, err := GetCurBlock()
|
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
|
// Create list of eligible unspent previous outputs to use as tx
|
||||||
// inputs, and sort by the amount in reverse order so a minimum number
|
// inputs, and sort by the amount in reverse order so a minimum number
|
||||||
// of inputs is needed.
|
// of inputs is needed.
|
||||||
eligible := make([]*tx.Credit, 0, len(utxos))
|
eligible := make([]*tx.Credit, 0, len(credits))
|
||||||
for _, utxo := range utxos {
|
for _, c := range credits {
|
||||||
if confirmed(minconf, utxo.BlockHeight, bs.Height) {
|
if c.Confirmed(minconf, bs.Height) {
|
||||||
// Coinbase transactions must have have reached maturity
|
// Coinbase transactions must have have reached maturity
|
||||||
// before their outputs may be spent.
|
// before their outputs may be spent.
|
||||||
if utxo.IsCoinbase() {
|
if c.IsCoinbase() {
|
||||||
confs := confirms(utxo.BlockHeight, bs.Height)
|
target := btcchain.CoinbaseMaturity
|
||||||
if confs < btcchain.CoinbaseMaturity {
|
if !c.Confirmed(target, bs.Height) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eligible = append(eligible, utxo)
|
eligible = append(eligible, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Sort(sort.Reverse(ByAmount(eligible)))
|
sort.Sort(sort.Reverse(ByAmount(eligible)))
|
||||||
|
|
|
@ -1057,7 +1057,7 @@ func GetTransaction(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
||||||
ret.BlockIndex = int64(first.Tx.Tx().Index())
|
ret.BlockIndex = int64(first.Tx.Tx().Index())
|
||||||
ret.BlockHash = txBlock.Hash.String()
|
ret.BlockHash = txBlock.Hash.String()
|
||||||
ret.BlockTime = txBlock.Time.Unix()
|
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.
|
// 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.
|
// Since we do not mine this currently is never the case.
|
||||||
|
|
19
tx/json.go
19
tx/json.go
|
@ -17,6 +17,7 @@
|
||||||
package tx
|
package tx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/conformal/btcchain"
|
||||||
"github.com/conformal/btcjson"
|
"github.com/conformal/btcjson"
|
||||||
"github.com/conformal/btcscript"
|
"github.com/conformal/btcscript"
|
||||||
"github.com/conformal/btcutil"
|
"github.com/conformal/btcutil"
|
||||||
|
@ -81,7 +82,7 @@ func (d *Debits) ToJSON(account string, chainHeight int32,
|
||||||
result.BlockHash = b.Hash.String()
|
result.BlockHash = b.Hash.String()
|
||||||
result.BlockIndex = int64(d.Tx().Index())
|
result.BlockIndex = int64(d.Tx().Index())
|
||||||
result.BlockTime = b.Time.Unix()
|
result.BlockTime = b.Time.Unix()
|
||||||
result.Confirmations = int64(confirms(b.Height, chainHeight))
|
result.Confirmations = int64(d.Confirmations(chainHeight))
|
||||||
}
|
}
|
||||||
reply = append(reply, result)
|
reply = append(reply, result)
|
||||||
}
|
}
|
||||||
|
@ -103,9 +104,21 @@ func (c *Credit) ToJSON(account string, chainHeight int32,
|
||||||
address = addrs[0].EncodeAddress()
|
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{
|
result := btcjson.ListTransactionsResult{
|
||||||
Account: account,
|
Account: account,
|
||||||
Category: "receive",
|
Category: category,
|
||||||
Address: address,
|
Address: address,
|
||||||
Amount: btcutil.Amount(txout.Value).ToUnit(btcutil.AmountBTC),
|
Amount: btcutil.Amount(txout.Value).ToUnit(btcutil.AmountBTC),
|
||||||
TxID: c.Tx().Sha().String(),
|
TxID: c.Tx().Sha().String(),
|
||||||
|
@ -122,7 +135,7 @@ func (c *Credit) ToJSON(account string, chainHeight int32,
|
||||||
result.BlockHash = b.Hash.String()
|
result.BlockHash = b.Hash.String()
|
||||||
result.BlockIndex = int64(c.Tx().Index())
|
result.BlockIndex = int64(c.Tx().Index())
|
||||||
result.BlockTime = b.Time.Unix()
|
result.BlockTime = b.Time.Unix()
|
||||||
result.Confirmations = int64(confirms(b.Height, chainHeight))
|
result.Confirmations = int64(c.Confirmations(chainHeight))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|
12
tx/tx.go
12
tx/tx.go
|
@ -1297,6 +1297,18 @@ func (c *Credit) Change() bool {
|
||||||
return c.txRecord.credits[c.OutputIndex].change
|
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.
|
// IsCoinbase returns whether the transaction is a coinbase.
|
||||||
func (t *TxRecord) IsCoinbase() bool {
|
func (t *TxRecord) IsCoinbase() bool {
|
||||||
return t.BlockHeight != -1 && t.BlockIndex == 0
|
return t.BlockHeight != -1 && t.BlockIndex == 0
|
||||||
|
|
Loading…
Reference in a new issue