Merge pull request #1554 from halseth/bitcoind-mempool-spends

Filter out bitcoind mempool spends
This commit is contained in:
Olaoluwa Osuntokun 2018-07-16 13:24:35 -07:00 committed by GitHub
commit 6dff599d21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 11 deletions

@ -352,16 +352,40 @@ func (b *BitcoindNotifier) handleRelevantTx(tx chain.RelevantTx, bestHeight int3
// TODO(roasbeef): after change to // TODO(roasbeef): after change to
// loadfilter, only notify on block // loadfilter, only notify on block
// inclusion? // inclusion?
confirmedSpend := false
if tx.Block != nil { if tx.Block != nil {
confirmedSpend = true
spendDetails.SpendingHeight = tx.Block.Height spendDetails.SpendingHeight = tx.Block.Height
} else { } else {
spendDetails.SpendingHeight = bestHeight + 1 spendDetails.SpendingHeight = bestHeight + 1
} }
for _, ntfn := range clients { // Keep spendNotifications that are
chainntnfs.Log.Infof("Dispatching "+ // waiting for a confirmation around.
// They will be notified when we find
// the spend within a block.
rem := make(map[uint64]*spendNotification)
for c, ntfn := range clients {
// If this is a mempool spend,
// and this client didn't want
// to be notified on mempool
// spends, store it for later.
if !confirmedSpend && !ntfn.mempool {
rem[c] = ntfn
continue
}
confStr := "unconfirmed"
if confirmedSpend {
confStr = "confirmed"
}
chainntnfs.Log.Infof("Dispatching %s "+
"spend notification for "+ "spend notification for "+
"outpoint=%v", ntfn.targetOutpoint) "outpoint=%v at height %v",
confStr, ntfn.targetOutpoint,
spendDetails.SpendingHeight)
ntfn.spendChan <- spendDetails ntfn.spendChan <- spendDetails
// Close spendChan to ensure that any calls to Cancel will not // Close spendChan to ensure that any calls to Cancel will not
@ -370,6 +394,12 @@ func (b *BitcoindNotifier) handleRelevantTx(tx chain.RelevantTx, bestHeight int3
close(ntfn.spendChan) close(ntfn.spendChan)
} }
delete(b.spendNotifications, prevOut) delete(b.spendNotifications, prevOut)
// If we had any clients left, add them
// back to the map.
if len(rem) > 0 {
b.spendNotifications[prevOut] = rem
}
} }
} }
} }
@ -530,6 +560,8 @@ type spendNotification struct {
spendID uint64 spendID uint64
heightHint uint32 heightHint uint32
mempool bool
} }
// spendCancel is a message sent to the BitcoindNotifier when a client wishes // spendCancel is a message sent to the BitcoindNotifier when a client wishes
@ -548,12 +580,13 @@ type spendCancel struct {
// across the 'Spend' channel. The heightHint should represent the earliest // across the 'Spend' channel. The heightHint should represent the earliest
// height in the chain where the transaction could have been spent in. // height in the chain where the transaction could have been spent in.
func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
heightHint uint32, _ bool) (*chainntnfs.SpendEvent, error) { heightHint uint32, mempool bool) (*chainntnfs.SpendEvent, error) {
ntfn := &spendNotification{ ntfn := &spendNotification{
targetOutpoint: outpoint, targetOutpoint: outpoint,
spendChan: make(chan *chainntnfs.SpendDetail, 1), spendChan: make(chan *chainntnfs.SpendDetail, 1),
spendID: atomic.AddUint64(&b.spendClientCounter, 1), spendID: atomic.AddUint64(&b.spendClientCounter, 1),
mempool: mempool,
} }
select { select {

@ -425,14 +425,30 @@ func testSpendNotification(miner *rpctest.Harness,
t.Fatalf("tx not relayed to miner: %v", err) t.Fatalf("tx not relayed to miner: %v", err)
} }
// Make sure notifications are not yet sent. // Make sure notifications are not yet sent. We launch a go routine for
// all the spend clients, such that we can wait for them all in
// parallel.
//
// Since bitcoind is at times very slow at notifying about txs in the
// mempool, we use a quite large timeout of 10 seconds.
// TODO(halseth): change this when mempool spends are removed.
mempoolSpendTimeout := 10 * time.Second
mempoolSpends := make(chan *chainntnfs.SpendDetail, numClients)
for _, c := range spendClients { for _, c := range spendClients {
select { go func(client *chainntnfs.SpendEvent) {
case <-c.Spend: select {
t.Fatalf("did not expect to get notification before " + case s := <-client.Spend:
"block was mined") mempoolSpends <- s
case <-time.After(50 * time.Millisecond): case <-time.After(mempoolSpendTimeout):
} }
}(c)
}
select {
case <-mempoolSpends:
t.Fatalf("did not expect to get notification before " +
"block was mined")
case <-time.After(mempoolSpendTimeout):
} }
// Now we mine a single block, which should include our spend. The // Now we mine a single block, which should include our spend. The