invoices: fix deadlock in invoice registry

This commit is contained in:
Andras Banki-Horvath 2022-03-11 19:51:13 +01:00
parent 74c44da315
commit c548a70e0d
No known key found for this signature in database
GPG Key ID: 80E5375C094198D8

View File

@ -939,12 +939,18 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
// Execute locked notify exit hop logic. // Execute locked notify exit hop logic.
i.Lock() i.Lock()
resolution, err := i.notifyExitHopHtlcLocked(&ctx, hodlChan) resolution, invoiceToExpire, err := i.notifyExitHopHtlcLocked(
&ctx, hodlChan,
)
i.Unlock() i.Unlock()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if invoiceToExpire != nil {
i.expiryWatcher.AddInvoices(invoiceToExpire)
}
switch r := resolution.(type) { switch r := resolution.(type) {
// The htlc is held. Start a timer outside the lock if the htlc should // The htlc is held. Start a timer outside the lock if the htlc should
// be auto-released, because otherwise a deadlock may happen with the // be auto-released, because otherwise a deadlock may happen with the
@ -975,10 +981,11 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
} }
// notifyExitHopHtlcLocked is the internal implementation of NotifyExitHopHtlc // notifyExitHopHtlcLocked is the internal implementation of NotifyExitHopHtlc
// that should be executed inside the registry lock. // that should be executed inside the registry lock. The returned invoiceExpiry
// (if not nil) needs to be added to the expiry watcher outside of the lock.
func (i *InvoiceRegistry) notifyExitHopHtlcLocked( func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
ctx *invoiceUpdateCtx, hodlChan chan<- interface{}) ( ctx *invoiceUpdateCtx, hodlChan chan<- interface{}) (
HtlcResolution, error) { HtlcResolution, invoiceExpiry, error) {
// We'll attempt to settle an invoice matching this rHash on disk (if // We'll attempt to settle an invoice matching this rHash on disk (if
// one exists). The callback will update the invoice state and/or htlcs. // one exists). The callback will update the invoice state and/or htlcs.
@ -1014,15 +1021,17 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
return NewFailResolution( return NewFailResolution(
ctx.circuitKey, ctx.currentHeight, ctx.circuitKey, ctx.currentHeight,
ResultInvoiceNotFound, ResultInvoiceNotFound,
), nil ), nil, nil
case nil: case nil:
default: default:
ctx.log(err.Error()) ctx.log(err.Error())
return nil, err return nil, nil, err
} }
var invoiceToExpire invoiceExpiry
switch res := resolution.(type) { switch res := resolution.(type) {
case *HtlcFailResolution: case *HtlcFailResolution:
// Inspect latest htlc state on the invoice. If it is found, // Inspect latest htlc state on the invoice. If it is found,
@ -1116,7 +1125,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
case *htlcAcceptResolution: case *htlcAcceptResolution:
invoiceHtlc, ok := invoice.Htlcs[ctx.circuitKey] invoiceHtlc, ok := invoice.Htlcs[ctx.circuitKey]
if !ok { if !ok {
return nil, fmt.Errorf("accepted htlc: %v not"+ return nil, nil, fmt.Errorf("accepted htlc: %v not"+
" present on invoice: %x", ctx.circuitKey, " present on invoice: %x", ctx.circuitKey,
ctx.hash[:]) ctx.hash[:])
} }
@ -1145,8 +1154,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
// possible that we MppTimeout the htlcs, and then our relevant // possible that we MppTimeout the htlcs, and then our relevant
// expiry height could change. // expiry height could change.
if res.outcome == resultAccepted { if res.outcome == resultAccepted {
expiry := makeInvoiceExpiry(ctx.hash, invoice) invoiceToExpire = makeInvoiceExpiry(ctx.hash, invoice)
i.expiryWatcher.AddInvoices(expiry)
} }
i.hodlSubscribe(hodlChan, ctx.circuitKey) i.hodlSubscribe(hodlChan, ctx.circuitKey)
@ -1169,7 +1177,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
i.notifyClients(ctx.hash, invoice, setID) i.notifyClients(ctx.hash, invoice, setID)
} }
return resolution, nil return resolution, invoiceToExpire, nil
} }
// SettleHodlInvoice sets the preimage of a hodl invoice. // SettleHodlInvoice sets the preimage of a hodl invoice.