chainntnfs/neutrinonotify: async call GetUtxo from RegisterSpendNtfn

Since GetUtxo is a potentially long running call, we would see
RegisterSpendNtfn block in some cases, in particular on starting the
chain watcher, causing lnd to hang on startup.

This commit makes the call to GetUtxo in a go routine, letting
RegisterSpendNtfn return immediately when the notification events are
created.
This commit is contained in:
Johan T. Halseth 2019-06-04 09:58:04 +02:00
parent 4068e78af6
commit 3a44574625
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26

@ -740,58 +740,69 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
} }
// With the filter updated, we'll dispatch our historical rescan to // With the filter updated, we'll dispatch our historical rescan to
// ensure we detect the spend if it happened in the past. We'll ensure // ensure we detect the spend if it happened in the past.
// that neutrino is caught up to the starting height before we attempt n.wg.Add(1)
// to fetch the UTXO from the chain. If we're behind, then we may miss a go func() {
// notification dispatch. defer n.wg.Done()
for {
n.bestBlockMtx.RLock()
currentHeight := uint32(n.bestBlock.Height)
n.bestBlockMtx.RUnlock()
if currentHeight >= historicalDispatch.StartHeight { // We'll ensure that neutrino is caught up to the starting
break // height before we attempt to fetch the UTXO from the chain.
// If we're behind, then we may miss a notification dispatch.
for {
n.bestBlockMtx.RLock()
currentHeight := uint32(n.bestBlock.Height)
n.bestBlockMtx.RUnlock()
if currentHeight >= historicalDispatch.StartHeight {
break
}
select {
case <-time.After(time.Millisecond * 200):
case <-n.quit:
return
}
} }
time.Sleep(time.Millisecond * 200) spendReport, err := n.p2pNode.GetUtxo(
} neutrino.WatchInputs(inputToWatch),
neutrino.StartBlock(&waddrmgr.BlockStamp{
spendReport, err := n.p2pNode.GetUtxo( Height: int32(historicalDispatch.StartHeight),
neutrino.WatchInputs(inputToWatch), }),
neutrino.StartBlock(&waddrmgr.BlockStamp{ neutrino.EndBlock(&waddrmgr.BlockStamp{
Height: int32(historicalDispatch.StartHeight), Height: int32(historicalDispatch.EndHeight),
}), }),
neutrino.EndBlock(&waddrmgr.BlockStamp{ neutrino.QuitChan(n.quit),
Height: int32(historicalDispatch.EndHeight), )
}), if err != nil && !strings.Contains(err.Error(), "not found") {
neutrino.QuitChan(n.quit), chainntnfs.Log.Errorf("Failed getting UTXO: %v", err)
) return
if err != nil && !strings.Contains(err.Error(), "not found") {
return nil, err
}
// If a spend report was returned, and the transaction is present, then
// this means that the output is already spent.
var spendDetails *chainntnfs.SpendDetail
if spendReport != nil && spendReport.SpendingTx != nil {
spendingTxHash := spendReport.SpendingTx.TxHash()
spendDetails = &chainntnfs.SpendDetail{
SpentOutPoint: &spendRequest.OutPoint,
SpenderTxHash: &spendingTxHash,
SpendingTx: spendReport.SpendingTx,
SpenderInputIndex: spendReport.SpendingInputIndex,
SpendingHeight: int32(spendReport.SpendingTxHeight),
} }
}
// Finally, no matter whether the rescan found a spend in the past or // If a spend report was returned, and the transaction is present, then
// not, we'll mark our historical rescan as complete to ensure the // this means that the output is already spent.
// outpoint's spend hint gets updated upon connected/disconnected var spendDetails *chainntnfs.SpendDetail
// blocks. if spendReport != nil && spendReport.SpendingTx != nil {
err = n.txNotifier.UpdateSpendDetails(spendRequest, spendDetails) spendingTxHash := spendReport.SpendingTx.TxHash()
if err != nil { spendDetails = &chainntnfs.SpendDetail{
return nil, err SpentOutPoint: &spendRequest.OutPoint,
} SpenderTxHash: &spendingTxHash,
SpendingTx: spendReport.SpendingTx,
SpenderInputIndex: spendReport.SpendingInputIndex,
SpendingHeight: int32(spendReport.SpendingTxHeight),
}
}
// Finally, no matter whether the rescan found a spend in the past or
// not, we'll mark our historical rescan as complete to ensure the
// outpoint's spend hint gets updated upon connected/disconnected
// blocks.
err = n.txNotifier.UpdateSpendDetails(spendRequest, spendDetails)
if err != nil {
chainntnfs.Log.Errorf("Failed to update spend details: %v", err)
return
}
}()
return ntfn.Event, nil return ntfn.Event, nil
} }