chainntnfs: properly account for partial historical confirmation dispatch

This commit modifies the recently added logic to the ChainNotifier to:
  1. Fix the off-by-one confirmation error that was missed due a flaky
test
  2. Ensure that partial historical confirmations are properly handled.

The partial hostile confirmation case arises when a transaction already
a non-zero number of confirmations when the notification is registered,
but less than what would trigger the confirmation notification. To fix
this, transaction which have a partial number of confirmation are now
properly inserted into the confHeap, skipping first first phase for
notifications.
This commit is contained in:
Olaoluwa Osuntokun committed 2016-12-08 16:16:29 -08:00
1 parent a20594b0bf
commit 1e3635b5aa
2 files changed
+105 -33

No files matched your search

+47 -17
View File
@@ -2,7 +2,6 @@ package btcdnotify
import (
"container/heap"
"strings"
"sync"
"sync/atomic"
"time"
@@ -212,6 +211,8 @@ func (b *BtcdNotifier) onRedeemingTx(tx *btcutil.Tx, details *btcjson.BlockDetai
// notificationDispatcher is the primary goroutine which handles client
// notification registrations, as well as notification dispatches.
func (b *BtcdNotifier) notificationDispatcher() {
var currentHeight int32
out:
for {
select {
@@ -226,9 +227,14 @@ out:
chainntnfs.Log.Infof("New confirmations "+
"subscription: txid=%v, numconfs=%v",
*msg.txid, msg.numConfirmations)
// TODO(roasbeef): perform a N-block look
// behind to catch race-condition due to faster
// inter-block time?
// If the notification can be partially or
// fully dispatched, then we can skip the first
// phase for ntfns.
if b.attemptHistoricalDispatch(msg, currentHeight) {
continue
}
txid := *msg.txid
b.confNotifications[txid] = append(b.confNotifications[txid], msg)
case *blockEpochRegistration:
@@ -252,6 +258,8 @@ out:
b.chainUpdates = b.chainUpdates[1:]
b.chainUpdateMtx.Unlock()
currentHeight = update.blockHeight
newBlock, err := b.chainConn.GetBlock(update.blockHash)
if err != nil {
chainntnfs.Log.Errorf("Unable to get block: %v", err)
@@ -329,6 +337,41 @@ out:
b.wg.Done()
}
// attemptHistoricalDispatch tries to use historical information to decide if a
// notificaiton ca be disptahced immediately, or is partially confirmed so it
// can skip straight to the confirmations heap.
func (b *BtcdNotifier) attemptHistoricalDispatch(msg *confirmationsNotification,
currentHeight int32) bool {
// If the transaction already has some or all of the confirmations,
// then we may be able to
// dispatch it immediately.
tx, err := b.chainConn.GetRawTransactionVerbose(msg.txid)
if err != nil {
return false
}
// If the transaction has more that enough confirmations, then we can
// dispatch it immediately after obtaininig for information w.r.t
// exaclty *when* if got all its confirmations.
if uint32(tx.Confirmations) >= msg.numConfirmations {
msg.finConf <- int32(tx.Confirmations)
return true
}
// Otherwise, the transaciton has only been *partially* confirmed, so
// we need to insert it into the confirmationheap.
confsLeft := msg.numConfirmations - uint32(tx.Confirmations)
confHeight := uint32(currentHeight) + confsLeft
heapEntry := &confEntry{
msg,
confHeight,
}
heap.Push(b.confHeap, heapEntry)
return false
}
// notifyBlockEpochs notifies all registered block epoch clients of the newly
// connected block to the main chain.
func (b *BtcdNotifier) notifyBlockEpochs(newHeight int32, newSha *wire.ShaHash) {
@@ -506,19 +549,6 @@ func (b *BtcdNotifier) RegisterConfirmationsNtfn(txid *wire.ShaHash,
b.notificationRegistry <- ntfn
// The following conditional checks transaction confirmation notification
// requests so that if the transaction has already been included in a block
// with the requested number of confirmations, the notification will be
// dispatched immediately.
tx, err := b.chainConn.GetRawTransactionVerbose(txid)
if err != nil {
if !strings.Contains(err.Error(), "No information") {
return nil, err
}
} else if uint32(tx.Confirmations) > numConfs {
ntfn.finConf <- int32(tx.Confirmations)
}
return &chainntnfs.ConfirmationEvent{
Confirmed: ntfn.finConf,
NegativeConf: ntfn.negativeConf,