Use the correct synced block when calculating confs.

This was previously using the most recently notified (by the chain
package) block, but transaction processing from this block may not be
finished yet.  Using this block's height to calculate the number of
confirmations is therefore incorrect, and can result in every RPC
handler missing transactions or returning transactions from the wrong
block.
This commit is contained in:
Josh Rickmar 2014-12-31 13:17:43 -05:00
parent c9d9b4b610
commit 5ad37374fb
2 changed files with 31 additions and 78 deletions

View file

@ -1057,13 +1057,8 @@ func (b blockDisconnected) notificationCmds(w *Wallet) []btcjson.Cmd {
}
func (c txCredit) notificationCmds(w *Wallet) []btcjson.Cmd {
bs, err := w.chainSvr.BlockStamp()
if err != nil {
log.Warnf("Dropping tx credit notification due to unknown "+
"chain height: %v", err)
return nil
}
ltr, err := txstore.Credit(c).ToJSON("", bs.Height, activeNet.Params)
blk := w.Manager.SyncedTo()
ltr, err := txstore.Credit(c).ToJSON("", blk.Height, activeNet.Params)
if err != nil {
log.Errorf("Cannot create notification for transaction "+
"credit: %v", err)
@ -1074,13 +1069,8 @@ func (c txCredit) notificationCmds(w *Wallet) []btcjson.Cmd {
}
func (d txDebit) notificationCmds(w *Wallet) []btcjson.Cmd {
bs, err := w.chainSvr.BlockStamp()
if err != nil {
log.Warnf("Dropping tx debit notification due to unknown "+
"chain height: %v", err)
return nil
}
ltrs, err := txstore.Debits(d).ToJSON("", bs.Height, activeNet.Params)
blk := w.Manager.SyncedTo()
ltrs, err := txstore.Debits(d).ToJSON("", blk.Height, activeNet.Params)
if err != nil {
log.Errorf("Cannot create notification for transaction "+
"debits: %v", err)
@ -1961,10 +1951,7 @@ func GetTransaction(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interf
return nil, btcjson.ErrNoTxInfo
}
bs, err := w.SyncedChainTip()
if err != nil {
return nil, err
}
blk := w.Manager.SyncedTo()
var txBuf bytes.Buffer
txBuf.Grow(record.Tx().MsgTx().SerializeSize())
@ -1991,7 +1978,7 @@ func GetTransaction(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interf
ret.BlockIndex = int64(record.Tx().Index())
ret.BlockHash = txBlock.Hash.String()
ret.BlockTime = txBlock.Time.Unix()
ret.Confirmations = int64(record.Confirmations(bs.Height))
ret.Confirmations = int64(record.Confirmations(blk.Height))
}
credits := record.Credits()
@ -2042,7 +2029,7 @@ func GetTransaction(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interf
ret.Details = append(ret.Details, btcjson.GetTransactionDetailsResult{
Account: "",
Category: cred.Category(bs.Height).String(),
Category: cred.Category(blk.Height).String(),
Amount: cred.Amount().ToUnit(btcutil.AmountBTC),
Address: addr,
})
@ -2087,10 +2074,7 @@ func ListLockUnspent(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (inter
func ListReceivedByAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.ListReceivedByAccountCmd)
bs, err := w.SyncedChainTip()
if err != nil {
return nil, err
}
blk := w.Manager.SyncedTo()
// Total amount received.
var amount btcutil.Amount
@ -2100,12 +2084,12 @@ func ListReceivedByAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd)
for _, record := range w.TxStore.Records() {
for _, credit := range record.Credits() {
if !credit.Confirmed(cmd.MinConf, bs.Height) {
if !credit.Confirmed(cmd.MinConf, blk.Height) {
// Not enough confirmations, skip the current block.
continue
}
amount += credit.Amount()
confirmations = credit.Confirmations(bs.Height)
confirmations = credit.Confirmations(blk.Height)
}
}
@ -2143,10 +2127,7 @@ func ListReceivedByAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd)
tx []string
}
bs, err := w.SyncedChainTip()
if err != nil {
return nil, err
}
blk := w.Manager.SyncedTo()
// Intermediate data for all addresses.
allAddrData := make(map[string]AddrData)
@ -2164,8 +2145,8 @@ func ListReceivedByAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd)
}
for _, record := range w.TxStore.Records() {
for _, credit := range record.Credits() {
confirmations := credit.Confirmations(bs.Height)
if !credit.Confirmed(cmd.MinConf, bs.Height) {
confirmations := credit.Confirmations(blk.Height)
if !credit.Confirmed(cmd.MinConf, blk.Height) {
// Not enough confirmations, skip the current block.
continue
}
@ -2228,20 +2209,14 @@ func ListSinceBlock(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interf
height = int32(block.Height())
}
bs, err := w.SyncedChainTip()
if err != nil {
return nil, err
}
blk := w.Manager.SyncedTo()
// For the result we need the block hash for the last block counted
// in the blockchain due to confirmations. We send this off now so that
// it can arrive asynchronously while we figure out the rest.
gbh := chainSvr.GetBlockHashAsync(int64(bs.Height) + 1 - int64(cmd.TargetConfirmations))
if err != nil {
return nil, err
}
gbh := chainSvr.GetBlockHashAsync(int64(blk.Height) + 1 - int64(cmd.TargetConfirmations))
txInfoList, err := w.ListSinceBlock(height, bs.Height,
txInfoList, err := w.ListSinceBlock(height, blk.Height,
cmd.TargetConfirmations)
if err != nil {
return nil, err

View file

@ -717,12 +717,8 @@ func (w *Wallet) AddressUsed(addr waddrmgr.ManagedAddress) bool {
// the balance will be calculated based on how many how many blocks
// include a UTXO.
func (w *Wallet) CalculateBalance(confirms int) (btcutil.Amount, error) {
bs, err := w.SyncedChainTip()
if err != nil {
return 0, err
}
return w.TxStore.Balance(confirms, bs.Height)
blk := w.Manager.SyncedTo()
return w.TxStore.Balance(confirms, blk.Height)
}
// CurrentAddress gets the most recently requested Bitcoin payment address
@ -782,16 +778,13 @@ func (w *Wallet) ListTransactions(from, count int) ([]btcjson.ListTransactionsRe
// Get current block. The block height used for calculating
// the number of tx confirmations.
bs, err := w.SyncedChainTip()
if err != nil {
return txList, err
}
blk := w.Manager.SyncedTo()
records := w.TxStore.Records()
lastLookupIdx := len(records) - count
// Search in reverse order: lookup most recently-added first.
for i := len(records) - 1; i >= from && i >= lastLookupIdx; i-- {
jsonResults, err := records[i].ToJSON("", bs.Height,
jsonResults, err := records[i].ToJSON("", blk.Height,
w.Manager.ChainParams())
if err != nil {
return nil, err
@ -812,10 +805,7 @@ func (w *Wallet) ListAddressTransactions(pkHashes map[string]struct{}) (
// Get current block. The block height used for calculating
// the number of tx confirmations.
bs, err := w.SyncedChainTip()
if err != nil {
return txList, err
}
blk := w.Manager.SyncedTo()
for _, r := range w.TxStore.Records() {
for _, c := range r.Credits() {
@ -833,7 +823,7 @@ func (w *Wallet) ListAddressTransactions(pkHashes map[string]struct{}) (
if _, ok := pkHashes[string(apkh.ScriptAddress())]; !ok {
continue
}
jsonResult, err := c.ToJSON("", bs.Height,
jsonResult, err := c.ToJSON("", blk.Height,
w.Manager.ChainParams())
if err != nil {
return nil, err
@ -853,15 +843,12 @@ func (w *Wallet) ListAllTransactions() ([]btcjson.ListTransactionsResult, error)
// Get current block. The block height used for calculating
// the number of tx confirmations.
bs, err := w.SyncedChainTip()
if err != nil {
return txList, err
}
blk := w.Manager.SyncedTo()
// Search in reverse order: lookup most recently-added first.
records := w.TxStore.Records()
for i := len(records) - 1; i >= 0; i-- {
jsonResults, err := records[i].ToJSON("", bs.Height,
jsonResults, err := records[i].ToJSON("", blk.Height,
w.Manager.ChainParams())
if err != nil {
return nil, err
@ -882,10 +869,7 @@ func (w *Wallet) ListUnspent(minconf, maxconf int,
results := []*btcjson.ListUnspentResult{}
bs, err := w.SyncedChainTip()
if err != nil {
return results, err
}
blk := w.Manager.SyncedTo()
filter := len(addresses) != 0
@ -895,12 +879,12 @@ func (w *Wallet) ListUnspent(minconf, maxconf int,
}
for _, credit := range unspent {
confs := credit.Confirmations(bs.Height)
confs := credit.Confirmations(blk.Height)
if int(confs) < minconf || int(confs) > maxconf {
continue
}
if credit.IsCoinbase() {
if !credit.Confirmed(blockchain.CoinbaseMaturity, bs.Height) {
if !credit.Confirmed(blockchain.CoinbaseMaturity, blk.Height) {
continue
}
}
@ -1245,10 +1229,7 @@ func (w *Wallet) NewChangeAddress() (btcutil.Address, error) {
// total amount of bitcoins received for any wallet address. Amounts received
// through multisig transactions are ignored.
func (w *Wallet) TotalReceived(confirms int) (btcutil.Amount, error) {
bs, err := w.SyncedChainTip()
if err != nil {
return 0, err
}
blk := w.Manager.SyncedTo()
var amount btcutil.Amount
for _, r := range w.TxStore.Records() {
@ -1259,7 +1240,7 @@ func (w *Wallet) TotalReceived(confirms int) (btcutil.Amount, error) {
}
// Tally if the appropiate number of block confirmations have passed.
if c.Confirmed(confirms, bs.Height) {
if c.Confirmed(confirms, blk.Height) {
amount += c.Amount()
}
}
@ -1271,16 +1252,13 @@ func (w *Wallet) TotalReceived(confirms int) (btcutil.Amount, error) {
// returning the total amount of bitcoins received for a single wallet
// address.
func (w *Wallet) TotalReceivedForAddr(addr btcutil.Address, confirms int) (btcutil.Amount, error) {
bs, err := w.SyncedChainTip()
if err != nil {
return 0, err
}
blk := w.Manager.SyncedTo()
addrStr := addr.EncodeAddress()
var amount btcutil.Amount
for _, r := range w.TxStore.Records() {
for _, c := range r.Credits() {
if !c.Confirmed(confirms, bs.Height) {
if !c.Confirmed(confirms, blk.Height) {
continue
}