Skip to content

Commit

Permalink
invoices: cancel keysend invoices after 20 seconds
Browse files Browse the repository at this point in the history
  • Loading branch information
joostjager committed Apr 9, 2020
1 parent c715183 commit 76c9316
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 9 deletions.
14 changes: 11 additions & 3 deletions invoices/invoice_expiry_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
type invoiceExpiry struct {
PaymentHash lntypes.Hash
Expiry time.Time
Keysend bool
}

// Less implements PriorityQueueItem.Less such that the top item in the
Expand All @@ -41,7 +42,7 @@ type InvoiceExpiryWatcher struct {
clock clock.Clock

// cancelInvoice is a template method that cancels an expired invoice.
cancelInvoice func(lntypes.Hash) error
cancelInvoice func(lntypes.Hash, bool) error

// expiryQueue holds invoiceExpiry items and is used to find the next
// invoice to expire.
Expand Down Expand Up @@ -71,7 +72,7 @@ func NewInvoiceExpiryWatcher(clock clock.Clock) *InvoiceExpiryWatcher {
// expects a cancellation function passed that will be use to cancel expired
// invoices by their payment hash.
func (ew *InvoiceExpiryWatcher) Start(
cancelInvoice func(lntypes.Hash) error) error {
cancelInvoice func(lntypes.Hash, bool) error) error {

ew.Lock()
defer ew.Unlock()
Expand Down Expand Up @@ -121,6 +122,7 @@ func (ew *InvoiceExpiryWatcher) prepareInvoice(
return &invoiceExpiry{
PaymentHash: paymentHash,
Expiry: expiry,
Keysend: len(invoice.PaymentRequest) == 0,
}
}

Expand Down Expand Up @@ -190,7 +192,13 @@ func (ew *InvoiceExpiryWatcher) cancelNextExpiredInvoice() {
return
}

err := ew.cancelInvoice(top.PaymentHash)
// Don't force-cancel already accepted invoices. An exception to
// this are auto-generated keysend invoices. Because those start
// out in the Accepted state, the expiry field would never be
// used. Enabling cancellation for accepted keysend invoices
// creates a safety mechanism that can prevents channel
// force-closes.
err := ew.cancelInvoice(top.PaymentHash, top.Keysend)
if err != nil && err != channeldb.ErrInvoiceAlreadySettled &&
err != channeldb.ErrInvoiceAlreadyCanceled {

Expand Down
6 changes: 4 additions & 2 deletions invoices/invoice_expiry_watcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ func newInvoiceExpiryWatcherTest(t *testing.T, now time.Time,
),
}

err := test.watcher.Start(func(paymentHash lntypes.Hash) error {
err := test.watcher.Start(func(paymentHash lntypes.Hash,
force bool) error {

test.canceledInvoices = append(test.canceledInvoices, paymentHash)
return nil
})
Expand Down Expand Up @@ -60,7 +62,7 @@ func (t *invoiceExpiryWatcherTest) checkExpectations() {
// Tests that InvoiceExpiryWatcher can be started and stopped.
func TestInvoiceExpiryWatcherStartStop(t *testing.T) {
watcher := NewInvoiceExpiryWatcher(clock.NewTestClock(testTime))
cancel := func(lntypes.Hash) error {
cancel := func(lntypes.Hash, bool) error {
t.Fatalf("unexpected call")
return nil
}
Expand Down
7 changes: 3 additions & 4 deletions invoices/invoiceregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,7 @@ func (i *InvoiceRegistry) populateExpiryWatcher() error {
func (i *InvoiceRegistry) Start() error {
// Start InvoiceExpiryWatcher and prepopulate it with existing active
// invoices.
err := i.expiryWatcher.Start(func(paymentHash lntypes.Hash) error {
cancelIfAccepted := false
return i.cancelInvoiceImpl(paymentHash, cancelIfAccepted)
})
err := i.expiryWatcher.Start(i.cancelInvoiceImpl)

if err != nil {
return err
Expand Down Expand Up @@ -694,7 +691,9 @@ func (i *InvoiceRegistry) processKeySend(ctx invoiceUpdateCtx) error {
Value: amt,
PaymentPreimage: &preimage,
Features: features,
Expiry: 20 * time.Second,
},
HodlInvoice: true,
}

// Insert invoice into database. Ignore duplicates, because this
Expand Down

0 comments on commit 76c9316

Please sign in to comment.