Skip to content

Commit

Permalink
Merge pull request lightningnetwork#6332 from bhandras/invoice_regist…
Browse files Browse the repository at this point in the history
…ry_deadlock

invoices: fix deadlock in invoice registry
  • Loading branch information
guggero authored Mar 17, 2022
2 parents f13399b + 95d8fd8 commit d287884
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 10 deletions.
5 changes: 4 additions & 1 deletion docs/release-notes/release-notes-0.15.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
validation](https://github.com/lightningnetwork/lnd/pull/6314) when calling
`NewSigFromRawSignature`.

* [Fixed deadlock in invoice
registry](https://github.com/lightningnetwork/lnd/pull/6332).


## Misc

Expand Down Expand Up @@ -183,4 +186,4 @@ gRPC performance metrics (latency to process `GetInfo`, etc)](https://github.com
* Thebora Kompanioni
* Torkel Rogstad
* Vsevolod Kaganovych
* Yong Yu
* Yong Yu
26 changes: 17 additions & 9 deletions invoices/invoiceregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -939,12 +939,18 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,

// Execute locked notify exit hop logic.
i.Lock()
resolution, err := i.notifyExitHopHtlcLocked(&ctx, hodlChan)
resolution, invoiceToExpire, err := i.notifyExitHopHtlcLocked(
&ctx, hodlChan,
)
i.Unlock()
if err != nil {
return nil, err
}

if invoiceToExpire != nil {
i.expiryWatcher.AddInvoices(invoiceToExpire)
}

switch r := resolution.(type) {
// 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
Expand Down Expand Up @@ -975,10 +981,11 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
}

// 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(
ctx *invoiceUpdateCtx, hodlChan chan<- interface{}) (
HtlcResolution, error) {
HtlcResolution, invoiceExpiry, error) {

// 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.
Expand Down Expand Up @@ -1014,15 +1021,17 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
return NewFailResolution(
ctx.circuitKey, ctx.currentHeight,
ResultInvoiceNotFound,
), nil
), nil, nil

case nil:

default:
ctx.log(err.Error())
return nil, err
return nil, nil, err
}

var invoiceToExpire invoiceExpiry

switch res := resolution.(type) {
case *HtlcFailResolution:
// Inspect latest htlc state on the invoice. If it is found,
Expand Down Expand Up @@ -1116,7 +1125,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
case *htlcAcceptResolution:
invoiceHtlc, ok := invoice.Htlcs[ctx.circuitKey]
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,
ctx.hash[:])
}
Expand Down Expand Up @@ -1145,8 +1154,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
// possible that we MppTimeout the htlcs, and then our relevant
// expiry height could change.
if res.outcome == resultAccepted {
expiry := makeInvoiceExpiry(ctx.hash, invoice)
i.expiryWatcher.AddInvoices(expiry)
invoiceToExpire = makeInvoiceExpiry(ctx.hash, invoice)
}

i.hodlSubscribe(hodlChan, ctx.circuitKey)
Expand All @@ -1169,7 +1177,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
i.notifyClients(ctx.hash, invoice, setID)
}

return resolution, nil
return resolution, invoiceToExpire, nil
}

// SettleHodlInvoice sets the preimage of a hodl invoice.
Expand Down

0 comments on commit d287884

Please sign in to comment.