mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
channeldb: add DeleteInvoices call
This commit extends channeldb with the DeleteInvoices call which is when passed a slice of delete references will attempt to delete the invoices pointed to by the references and also clean up all our invoice indexes.
This commit is contained in:
parent
92f3b0a30c
commit
a5778c4673
@ -1151,3 +1151,96 @@ func TestInvoiceRef(t *testing.T) {
|
|||||||
require.Equal(t, payHash, refByHashAndAddr.PayHash())
|
require.Equal(t, payHash, refByHashAndAddr.PayHash())
|
||||||
require.Equal(t, &payAddr, refByHashAndAddr.PayAddr())
|
require.Equal(t, &payAddr, refByHashAndAddr.PayAddr())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDeleteInvoices tests that deleting a list of invoices will succeed
|
||||||
|
// if all delete references are valid, or will fail otherwise.
|
||||||
|
func TestDeleteInvoices(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db, cleanup, err := MakeTestDB()
|
||||||
|
defer cleanup()
|
||||||
|
require.NoError(t, err, "unable to make test db")
|
||||||
|
|
||||||
|
// Add some invoices to the test db.
|
||||||
|
numInvoices := 3
|
||||||
|
invoicesToDelete := make([]InvoiceDeleteRef, numInvoices)
|
||||||
|
|
||||||
|
for i := 0; i < numInvoices; i++ {
|
||||||
|
invoice, err := randInvoice(lnwire.MilliSatoshi(i + 1))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
paymentHash := invoice.Terms.PaymentPreimage.Hash()
|
||||||
|
addIndex, err := db.AddInvoice(invoice, paymentHash)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Settle the second invoice.
|
||||||
|
if i == 1 {
|
||||||
|
invoice, err = db.UpdateInvoice(
|
||||||
|
InvoiceRefByHash(paymentHash),
|
||||||
|
getUpdateInvoice(invoice.Terms.Value),
|
||||||
|
)
|
||||||
|
require.NoError(t, err, "unable to settle invoice")
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the delete ref for later.
|
||||||
|
invoicesToDelete[i] = InvoiceDeleteRef{
|
||||||
|
PayHash: paymentHash,
|
||||||
|
PayAddr: &invoice.Terms.PaymentAddr,
|
||||||
|
AddIndex: addIndex,
|
||||||
|
SettleIndex: invoice.SettleIndex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertInvoiceCount asserts that the number of invoices equals
|
||||||
|
// to the passed count.
|
||||||
|
assertInvoiceCount := func(count int) {
|
||||||
|
// Query to collect all invoices.
|
||||||
|
query := InvoiceQuery{
|
||||||
|
IndexOffset: 0,
|
||||||
|
NumMaxInvoices: math.MaxUint64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we really have 3 invoices.
|
||||||
|
response, err := db.QueryInvoices(query)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, count, len(response.Invoices))
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOR one byte of one of the references' hash and attempt to delete.
|
||||||
|
invoicesToDelete[0].PayHash[2] ^= 3
|
||||||
|
require.Error(t, db.DeleteInvoice(invoicesToDelete))
|
||||||
|
assertInvoiceCount(3)
|
||||||
|
|
||||||
|
// Restore the hash.
|
||||||
|
invoicesToDelete[0].PayHash[2] ^= 3
|
||||||
|
|
||||||
|
// XOR one byte of one of the references' payment address and attempt
|
||||||
|
// to delete.
|
||||||
|
invoicesToDelete[1].PayAddr[5] ^= 7
|
||||||
|
require.Error(t, db.DeleteInvoice(invoicesToDelete))
|
||||||
|
assertInvoiceCount(3)
|
||||||
|
|
||||||
|
// Restore the payment address.
|
||||||
|
invoicesToDelete[1].PayAddr[5] ^= 7
|
||||||
|
|
||||||
|
// XOR the second invoice's payment settle index as it is settled, and
|
||||||
|
// attempt to delete.
|
||||||
|
invoicesToDelete[1].SettleIndex ^= 11
|
||||||
|
require.Error(t, db.DeleteInvoice(invoicesToDelete))
|
||||||
|
assertInvoiceCount(3)
|
||||||
|
|
||||||
|
// Restore the settle index.
|
||||||
|
invoicesToDelete[1].SettleIndex ^= 11
|
||||||
|
|
||||||
|
// XOR the add index for one of the references and attempt to delete.
|
||||||
|
invoicesToDelete[2].AddIndex ^= 13
|
||||||
|
require.Error(t, db.DeleteInvoice(invoicesToDelete))
|
||||||
|
assertInvoiceCount(3)
|
||||||
|
|
||||||
|
// Restore the add index.
|
||||||
|
invoicesToDelete[2].AddIndex ^= 13
|
||||||
|
|
||||||
|
// Delete should succeed with all the valid references.
|
||||||
|
require.NoError(t, db.DeleteInvoice(invoicesToDelete))
|
||||||
|
assertInvoiceCount(0)
|
||||||
|
}
|
||||||
|
@ -1740,3 +1740,134 @@ func setSettleMetaFields(settleIndex kvdb.RwBucket, invoiceNum []byte,
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InvoiceDeleteRef holds a refererence to an invoice to be deleted.
|
||||||
|
type InvoiceDeleteRef struct {
|
||||||
|
// PayHash is the payment hash of the target invoice. All invoices are
|
||||||
|
// currently indexed by payment hash.
|
||||||
|
PayHash lntypes.Hash
|
||||||
|
|
||||||
|
// PayAddr is the payment addr of the target invoice. Newer invoices
|
||||||
|
// (0.11 and up) are indexed by payment address in addition to payment
|
||||||
|
// hash, but pre 0.8 invoices do not have one at all.
|
||||||
|
PayAddr *[32]byte
|
||||||
|
|
||||||
|
// AddIndex is the add index of the invoice.
|
||||||
|
AddIndex uint64
|
||||||
|
|
||||||
|
// SettleIndex is the settle index of the invoice.
|
||||||
|
SettleIndex uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (d *DB) DeleteInvoice(invoicesToDelete []InvoiceDeleteRef) error {
|
||||||
|
err := kvdb.Update(d, func(tx kvdb.RwTx) error {
|
||||||
|
invoices := tx.ReadWriteBucket(invoiceBucket)
|
||||||
|
if invoices == nil {
|
||||||
|
return ErrNoInvoicesCreated
|
||||||
|
}
|
||||||
|
|
||||||
|
invoiceIndex := invoices.NestedReadWriteBucket(
|
||||||
|
invoiceIndexBucket,
|
||||||
|
)
|
||||||
|
if invoiceIndex == nil {
|
||||||
|
return ErrNoInvoicesCreated
|
||||||
|
}
|
||||||
|
|
||||||
|
invoiceAddIndex := invoices.NestedReadWriteBucket(
|
||||||
|
addIndexBucket,
|
||||||
|
)
|
||||||
|
if invoiceAddIndex == nil {
|
||||||
|
return ErrNoInvoicesCreated
|
||||||
|
}
|
||||||
|
// settleIndex can be nil, as the bucket is created lazily
|
||||||
|
// when the first invoice is settled.
|
||||||
|
settleIndex := invoices.NestedReadWriteBucket(settleIndexBucket)
|
||||||
|
|
||||||
|
payAddrIndex := tx.ReadWriteBucket(payAddrIndexBucket)
|
||||||
|
|
||||||
|
for _, ref := range invoicesToDelete {
|
||||||
|
// Fetch the invoice key for using it to check for
|
||||||
|
// consistency and also to delete from the invoice index.
|
||||||
|
invoiceKey := invoiceIndex.Get(ref.PayHash[:])
|
||||||
|
if invoiceKey == nil {
|
||||||
|
return ErrInvoiceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
err := invoiceIndex.Delete(ref.PayHash[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete payment address index reference if there's a
|
||||||
|
// valid payment address passed.
|
||||||
|
if ref.PayAddr != nil {
|
||||||
|
// To ensure consistency check that the already
|
||||||
|
// fetched invoice key matches the one in the
|
||||||
|
// payment address index.
|
||||||
|
key := payAddrIndex.Get(ref.PayAddr[:])
|
||||||
|
if !bytes.Equal(key, invoiceKey) {
|
||||||
|
return fmt.Errorf("unknown invoice")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete from the payment address index.
|
||||||
|
err := payAddrIndex.Delete(ref.PayAddr[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var addIndexKey [8]byte
|
||||||
|
byteOrder.PutUint64(addIndexKey[:], ref.AddIndex)
|
||||||
|
|
||||||
|
// To ensure consistency check that the key stored in
|
||||||
|
// the add index also matches the previously fetched
|
||||||
|
// invoice key.
|
||||||
|
key := invoiceAddIndex.Get(addIndexKey[:])
|
||||||
|
if !bytes.Equal(key, invoiceKey) {
|
||||||
|
return fmt.Errorf("unknown invoice")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from the add index.
|
||||||
|
err = invoiceAddIndex.Delete(addIndexKey[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from the settle index if available and
|
||||||
|
// if the invoice is settled.
|
||||||
|
if settleIndex != nil && ref.SettleIndex > 0 {
|
||||||
|
var settleIndexKey [8]byte
|
||||||
|
byteOrder.PutUint64(
|
||||||
|
settleIndexKey[:], ref.SettleIndex,
|
||||||
|
)
|
||||||
|
|
||||||
|
// To ensure consistency check that the already
|
||||||
|
// fetched invoice key matches the one in the
|
||||||
|
// settle index
|
||||||
|
key := settleIndex.Get(settleIndexKey[:])
|
||||||
|
if !bytes.Equal(key, invoiceKey) {
|
||||||
|
return fmt.Errorf("unknown invoice")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = settleIndex.Delete(settleIndexKey[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally remove the serialized invoice from the
|
||||||
|
// invoice bucket.
|
||||||
|
err = invoices.Delete(invoiceKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user