channeldb: add k/v implementation for InvoiceDB.DeleteCanceledInvoices method

This commit is contained in:
Andras Banki-Horvath
2023-10-10 22:23:50 +02:00
parent 1f8065de35
commit 8ab267ad90
4 changed files with 165 additions and 0 deletions

View File

@@ -3146,6 +3146,65 @@ func TestDeleteInvoices(t *testing.T) {
assertInvoiceCount(0)
}
// TestDeleteCanceledInvoices tests that deleting canceled invoices with the
// specific DeleteCanceledInvoices method works correctly.
func TestDeleteCanceledInvoices(t *testing.T) {
t.Parallel()
db, err := MakeTestInvoiceDB(t)
require.NoError(t, err, "unable to make test db")
// Updatefunc is used to cancel an invoice.
updateFunc := func(invoice *invpkg.Invoice) (
*invpkg.InvoiceUpdateDesc, error) {
return &invpkg.InvoiceUpdateDesc{
UpdateType: invpkg.CancelInvoiceUpdate,
State: &invpkg.InvoiceStateUpdateDesc{
NewState: invpkg.ContractCanceled,
},
}, nil
}
// Add some invoices to the test db.
ctxb := context.Background()
var invoices []invpkg.Invoice
for i := 0; i < 10; i++ {
invoice, err := randInvoice(lnwire.MilliSatoshi(i + 1))
require.NoError(t, err)
paymentHash := invoice.Terms.PaymentPreimage.Hash()
_, err = db.AddInvoice(ctxb, invoice, paymentHash)
require.NoError(t, err)
// Cancel every second invoice.
if i%2 == 0 {
invoice, err = db.UpdateInvoice(
ctxb, invpkg.InvoiceRefByHash(paymentHash), nil,
updateFunc,
)
require.NoError(t, err)
} else {
invoices = append(invoices, *invoice)
}
}
// Delete canceled invoices.
require.NoError(t, db.DeleteCanceledInvoices(ctxb))
// Query to collect all invoices.
query := invpkg.InvoiceQuery{
IndexOffset: 0,
NumMaxInvoices: math.MaxUint64,
}
dbInvoices, err := db.QueryInvoices(ctxb, query)
require.NoError(t, err)
// Check that we really have the expected invoices.
require.Equal(t, invoices, dbInvoices.Invoices)
}
// TestAddInvoiceInvalidFeatureDeps asserts that inserting an invoice with
// invalid transitive feature dependencies fails with the appropriate error.
func TestAddInvoiceInvalidFeatureDeps(t *testing.T) {

View File

@@ -2761,6 +2761,102 @@ func delAMPSettleIndex(invoiceNum []byte, invoices,
return nil
}
// DeleteCanceledInvoices deletes all canceled invoices from the database.
func (d *DB) DeleteCanceledInvoices(_ context.Context) error {
return kvdb.Update(d, func(tx kvdb.RwTx) error {
invoices := tx.ReadWriteBucket(invoiceBucket)
if invoices == nil {
return nil
}
invoiceIndex := invoices.NestedReadWriteBucket(
invoiceIndexBucket,
)
if invoiceIndex == nil {
return invpkg.ErrNoInvoicesCreated
}
invoiceAddIndex := invoices.NestedReadWriteBucket(
addIndexBucket,
)
if invoiceAddIndex == nil {
return invpkg.ErrNoInvoicesCreated
}
payAddrIndex := tx.ReadWriteBucket(payAddrIndexBucket)
return invoiceIndex.ForEach(func(k, v []byte) error {
// Skip the special numInvoicesKey as that does not
// point to a valid invoice.
if bytes.Equal(k, numInvoicesKey) {
return nil
}
// Skip sub-buckets.
if v == nil {
return nil
}
invoice, err := fetchInvoice(v, invoices)
if err != nil {
return err
}
if invoice.State != invpkg.ContractCanceled {
return nil
}
// Delete the payment hash from the invoice index.
err = invoiceIndex.Delete(k)
if err != nil {
return err
}
// Delete payment address index reference if there's a
// valid payment address.
if invoice.Terms.PaymentAddr != invpkg.BlankPayAddr {
// To ensure consistency check that the already
// fetched invoice key matches the one in the
// payment address index.
key := payAddrIndex.Get(
invoice.Terms.PaymentAddr[:],
)
if bytes.Equal(key, k) {
// Delete from the payment address
// index.
if err := payAddrIndex.Delete(
invoice.Terms.PaymentAddr[:],
); err != nil {
return err
}
}
}
// Remove from the add index.
var addIndexKey [8]byte
byteOrder.PutUint64(addIndexKey[:], invoice.AddIndex)
err = invoiceAddIndex.Delete(addIndexKey[:])
if err != nil {
return err
}
// Note that we don't need to delete the invoice from
// the settle index as it is not added until the
// invoice is settled.
// Now remove all sub invoices.
err = delAMPInvoices(k, invoices)
if err != nil {
return err
}
// Finally remove the serialized invoice from the
// invoice bucket.
return invoices.Delete(k)
})
}, func() {})
}
// DeleteInvoice attempts to delete the passed invoices from the database in
// one transaction. The passed delete references hold all keys required to
// delete the invoices without also needing to deserialze them.

View File

@@ -95,6 +95,10 @@ type InvoiceDB interface {
// deserialze them.
DeleteInvoice(ctx context.Context,
invoicesToDelete []InvoiceDeleteRef) error
// DeleteCanceledInvoices removes all canceled invoices from the
// database.
DeleteCanceledInvoices(ctx context.Context) error
}
// Payload abstracts access to any additional fields provided in the final hop's

View File

@@ -85,3 +85,9 @@ func (m *MockInvoiceDB) DeleteInvoice(invoices []InvoiceDeleteRef) error {
return args.Error(0)
}
func (m *MockInvoiceDB) DeleteCanceledInvoices(ctx context.Context) error {
args := m.Called(ctx)
return args.Error(0)
}