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 { func (c txCredit) notificationCmds(w *Wallet) []btcjson.Cmd {
bs, err := w.chainSvr.BlockStamp() blk := w.Manager.SyncedTo()
if err != nil { ltr, err := txstore.Credit(c).ToJSON("", blk.Height, activeNet.Params)
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)
if err != nil { if err != nil {
log.Errorf("Cannot create notification for transaction "+ log.Errorf("Cannot create notification for transaction "+
"credit: %v", err) "credit: %v", err)
@ -1074,13 +1069,8 @@ func (c txCredit) notificationCmds(w *Wallet) []btcjson.Cmd {
} }
func (d txDebit) notificationCmds(w *Wallet) []btcjson.Cmd { func (d txDebit) notificationCmds(w *Wallet) []btcjson.Cmd {
bs, err := w.chainSvr.BlockStamp() blk := w.Manager.SyncedTo()
if err != nil { ltrs, err := txstore.Debits(d).ToJSON("", blk.Height, activeNet.Params)
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)
if err != nil { if err != nil {
log.Errorf("Cannot create notification for transaction "+ log.Errorf("Cannot create notification for transaction "+
"debits: %v", err) "debits: %v", err)
@ -1961,10 +1951,7 @@ func GetTransaction(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interf
return nil, btcjson.ErrNoTxInfo return nil, btcjson.ErrNoTxInfo
} }
bs, err := w.SyncedChainTip() blk := w.Manager.SyncedTo()
if err != nil {
return nil, err
}
var txBuf bytes.Buffer var txBuf bytes.Buffer
txBuf.Grow(record.Tx().MsgTx().SerializeSize()) 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.BlockIndex = int64(record.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(record.Confirmations(bs.Height)) ret.Confirmations = int64(record.Confirmations(blk.Height))
} }
credits := record.Credits() 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{ ret.Details = append(ret.Details, btcjson.GetTransactionDetailsResult{
Account: "", Account: "",
Category: cred.Category(bs.Height).String(), Category: cred.Category(blk.Height).String(),
Amount: cred.Amount().ToUnit(btcutil.AmountBTC), Amount: cred.Amount().ToUnit(btcutil.AmountBTC),
Address: addr, 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) { func ListReceivedByAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.ListReceivedByAccountCmd) cmd := icmd.(*btcjson.ListReceivedByAccountCmd)
bs, err := w.SyncedChainTip() blk := w.Manager.SyncedTo()
if err != nil {
return nil, err
}
// Total amount received. // Total amount received.
var amount btcutil.Amount 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 _, record := range w.TxStore.Records() {
for _, credit := range record.Credits() { 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. // Not enough confirmations, skip the current block.
continue continue
} }
amount += credit.Amount() 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 tx []string
} }
bs, err := w.SyncedChainTip() blk := w.Manager.SyncedTo()
if err != nil {
return nil, err
}
// Intermediate data for all addresses. // Intermediate data for all addresses.
allAddrData := make(map[string]AddrData) 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 _, record := range w.TxStore.Records() {
for _, credit := range record.Credits() { for _, credit := range record.Credits() {
confirmations := credit.Confirmations(bs.Height) confirmations := credit.Confirmations(blk.Height)
if !credit.Confirmed(cmd.MinConf, bs.Height) { if !credit.Confirmed(cmd.MinConf, blk.Height) {
// Not enough confirmations, skip the current block. // Not enough confirmations, skip the current block.
continue continue
} }
@ -2228,20 +2209,14 @@ func ListSinceBlock(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interf
height = int32(block.Height()) height = int32(block.Height())
} }
bs, err := w.SyncedChainTip() blk := w.Manager.SyncedTo()
if err != nil {
return nil, err
}
// For the result we need the block hash for the last block counted // 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 // in the blockchain due to confirmations. We send this off now so that
// it can arrive asynchronously while we figure out the rest. // it can arrive asynchronously while we figure out the rest.
gbh := chainSvr.GetBlockHashAsync(int64(bs.Height) + 1 - int64(cmd.TargetConfirmations)) gbh := chainSvr.GetBlockHashAsync(int64(blk.Height) + 1 - int64(cmd.TargetConfirmations))
if err != nil {
return nil, err
}
txInfoList, err := w.ListSinceBlock(height, bs.Height, txInfoList, err := w.ListSinceBlock(height, blk.Height,
cmd.TargetConfirmations) cmd.TargetConfirmations)
if err != nil { if err != nil {
return nil, err 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 // the balance will be calculated based on how many how many blocks
// include a UTXO. // include a UTXO.
func (w *Wallet) CalculateBalance(confirms int) (btcutil.Amount, error) { func (w *Wallet) CalculateBalance(confirms int) (btcutil.Amount, error) {
bs, err := w.SyncedChainTip() blk := w.Manager.SyncedTo()
if err != nil { return w.TxStore.Balance(confirms, blk.Height)
return 0, err
}
return w.TxStore.Balance(confirms, bs.Height)
} }
// CurrentAddress gets the most recently requested Bitcoin payment address // 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 // Get current block. The block height used for calculating
// the number of tx confirmations. // the number of tx confirmations.
bs, err := w.SyncedChainTip() blk := w.Manager.SyncedTo()
if err != nil {
return txList, err
}
records := w.TxStore.Records() records := w.TxStore.Records()
lastLookupIdx := len(records) - count lastLookupIdx := len(records) - count
// Search in reverse order: lookup most recently-added first. // Search in reverse order: lookup most recently-added first.
for i := len(records) - 1; i >= from && i >= lastLookupIdx; i-- { 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()) w.Manager.ChainParams())
if err != nil { if err != nil {
return nil, err return nil, err
@ -812,10 +805,7 @@ func (w *Wallet) ListAddressTransactions(pkHashes map[string]struct{}) (
// Get current block. The block height used for calculating // Get current block. The block height used for calculating
// the number of tx confirmations. // the number of tx confirmations.
bs, err := w.SyncedChainTip() blk := w.Manager.SyncedTo()
if err != nil {
return txList, err
}
for _, r := range w.TxStore.Records() { for _, r := range w.TxStore.Records() {
for _, c := range r.Credits() { for _, c := range r.Credits() {
@ -833,7 +823,7 @@ func (w *Wallet) ListAddressTransactions(pkHashes map[string]struct{}) (
if _, ok := pkHashes[string(apkh.ScriptAddress())]; !ok { if _, ok := pkHashes[string(apkh.ScriptAddress())]; !ok {
continue continue
} }
jsonResult, err := c.ToJSON("", bs.Height, jsonResult, err := c.ToJSON("", blk.Height,
w.Manager.ChainParams()) w.Manager.ChainParams())
if err != nil { if err != nil {
return nil, err return nil, err
@ -853,15 +843,12 @@ func (w *Wallet) ListAllTransactions() ([]btcjson.ListTransactionsResult, error)
// Get current block. The block height used for calculating // Get current block. The block height used for calculating
// the number of tx confirmations. // the number of tx confirmations.
bs, err := w.SyncedChainTip() blk := w.Manager.SyncedTo()
if err != nil {
return txList, err
}
// Search in reverse order: lookup most recently-added first. // Search in reverse order: lookup most recently-added first.
records := w.TxStore.Records() records := w.TxStore.Records()
for i := len(records) - 1; i >= 0; i-- { 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()) w.Manager.ChainParams())
if err != nil { if err != nil {
return nil, err return nil, err
@ -882,10 +869,7 @@ func (w *Wallet) ListUnspent(minconf, maxconf int,
results := []*btcjson.ListUnspentResult{} results := []*btcjson.ListUnspentResult{}
bs, err := w.SyncedChainTip() blk := w.Manager.SyncedTo()
if err != nil {
return results, err
}
filter := len(addresses) != 0 filter := len(addresses) != 0
@ -895,12 +879,12 @@ func (w *Wallet) ListUnspent(minconf, maxconf int,
} }
for _, credit := range unspent { for _, credit := range unspent {
confs := credit.Confirmations(bs.Height) confs := credit.Confirmations(blk.Height)
if int(confs) < minconf || int(confs) > maxconf { if int(confs) < minconf || int(confs) > maxconf {
continue continue
} }
if credit.IsCoinbase() { if credit.IsCoinbase() {
if !credit.Confirmed(blockchain.CoinbaseMaturity, bs.Height) { if !credit.Confirmed(blockchain.CoinbaseMaturity, blk.Height) {
continue continue
} }
} }
@ -1245,10 +1229,7 @@ func (w *Wallet) NewChangeAddress() (btcutil.Address, error) {
// total amount of bitcoins received for any wallet address. Amounts received // total amount of bitcoins received for any wallet address. Amounts received
// through multisig transactions are ignored. // through multisig transactions are ignored.
func (w *Wallet) TotalReceived(confirms int) (btcutil.Amount, error) { func (w *Wallet) TotalReceived(confirms int) (btcutil.Amount, error) {
bs, err := w.SyncedChainTip() blk := w.Manager.SyncedTo()
if err != nil {
return 0, err
}
var amount btcutil.Amount var amount btcutil.Amount
for _, r := range w.TxStore.Records() { 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. // 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() 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 // returning the total amount of bitcoins received for a single wallet
// address. // address.
func (w *Wallet) TotalReceivedForAddr(addr btcutil.Address, confirms int) (btcutil.Amount, error) { func (w *Wallet) TotalReceivedForAddr(addr btcutil.Address, confirms int) (btcutil.Amount, error) {
bs, err := w.SyncedChainTip() blk := w.Manager.SyncedTo()
if err != nil {
return 0, err
}
addrStr := addr.EncodeAddress() addrStr := addr.EncodeAddress()
var amount btcutil.Amount var amount btcutil.Amount
for _, r := range w.TxStore.Records() { for _, r := range w.TxStore.Records() {
for _, c := range r.Credits() { for _, c := range r.Credits() {
if !c.Confirmed(confirms, bs.Height) { if !c.Confirmed(confirms, blk.Height) {
continue continue
} }