diff --git a/channeldb/invoice_test.go b/channeldb/invoice_test.go index 68512607..3f35586b 100644 --- a/channeldb/invoice_test.go +++ b/channeldb/invoice_test.go @@ -359,10 +359,10 @@ func TestDuplicateSettleInvoice(t *testing.T) { } // If we try to settle the invoice again, then we should get the very - // same invoice back. + // same invoice back, but with an error this time. dbInvoice, err = db.SettleInvoice(payHash, amt) - if err != nil { - t.Fatalf("unable to settle invoice: %v", err) + if err != ErrInvoiceAlreadySettled { + t.Fatalf("expected ErrInvoiceAlreadySettled") } if dbInvoice == nil { diff --git a/channeldb/invoices.go b/channeldb/invoices.go index d7396376..c5223b17 100644 --- a/channeldb/invoices.go +++ b/channeldb/invoices.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/sha256" "encoding/binary" + "errors" "fmt" "io" "time" @@ -57,6 +58,10 @@ var ( // // settleIndexNo => invoiceKey settleIndexBucket = []byte("invoice-settle-index") + + // ErrInvoiceAlreadySettled is returned when the invoice is already + // settled. + ErrInvoiceAlreadySettled = errors.New("invoice already settled") ) const ( @@ -626,21 +631,14 @@ func (d *DB) SettleInvoice(paymentHash [32]byte, return ErrInvoiceNotFound } - invoice, err := settleInvoice( + settledInvoice, err = settleInvoice( invoices, settleIndex, invoiceNum, amtPaid, ) - if err != nil { - return err - } - settledInvoice = invoice - return nil + return err }) - if err != nil { - return nil, err - } - return settledInvoice, nil + return settledInvoice, err } // InvoicesSettledSince can be used by callers to catch up any settled invoices @@ -898,10 +896,8 @@ func settleInvoice(invoices, settleIndex *bbolt.Bucket, invoiceNum []byte, return nil, err } - // Add idempotency to duplicate settles, return here to avoid - // overwriting the previous info. if invoice.Terms.State == ContractSettled { - return &invoice, nil + return &invoice, ErrInvoiceAlreadySettled } // Now that we know the invoice hasn't already been settled, we'll diff --git a/invoices/invoiceregistry.go b/invoices/invoiceregistry.go index c4e45fe2..a34f7877 100644 --- a/invoices/invoiceregistry.go +++ b/invoices/invoiceregistry.go @@ -378,6 +378,14 @@ func (i *InvoiceRegistry) SettleInvoice(rHash lntypes.Hash, // If this isn't a debug invoice, then we'll attempt to settle an // invoice matching this rHash on disk (if one exists). invoice, err := i.cdb.SettleInvoice(rHash, amtPaid) + + // Implement idempotency by returning success if the invoice was already + // settled. + if err == channeldb.ErrInvoiceAlreadySettled { + log.Debugf("Invoice %v already settled", rHash) + return nil + } + if err != nil { return err }