diff --git a/accounts/interface.go b/accounts/interface.go index 533f05c7c..b2e11b3e0 100644 --- a/accounts/interface.go +++ b/accounts/interface.go @@ -230,6 +230,12 @@ type Store interface { status lnrpc.Payment_PaymentStatus, options ...UpsertPaymentOption) (bool, error) + // DeleteAccountPayment removes a payment entry from the account with + // the given ID. It will return an error if the payment is not + // associated with the account. + DeleteAccountPayment(_ context.Context, id AccountID, + hash lntypes.Hash) error + // RemoveAccount finds an account by its ID and removes it from the¨ // store. RemoveAccount(ctx context.Context, id AccountID) error diff --git a/accounts/service.go b/accounts/service.go index 6b411a9a8..de516f5a6 100644 --- a/accounts/service.go +++ b/accounts/service.go @@ -467,26 +467,7 @@ func (s *InterceptorService) PaymentErrored(ctx context.Context, id AccountID, "has already started") } - account, err := s.store.Account(ctx, id) - if err != nil { - return err - } - - // Check that this payment is actually associated with this account. - _, ok = account.Payments[hash] - if !ok { - return fmt.Errorf("payment with hash %s is not associated "+ - "with this account", hash) - } - - // Delete the payment and update the persisted account. - delete(account.Payments, hash) - - if err := s.store.UpdateAccount(ctx, account); err != nil { - return fmt.Errorf("error updating account: %w", err) - } - - return nil + return s.store.DeleteAccountPayment(ctx, id, hash) } // AssociatePayment associates a payment (hash) with the given account, diff --git a/accounts/store_kvdb.go b/accounts/store_kvdb.go index e9702d5be..18115e75e 100644 --- a/accounts/store_kvdb.go +++ b/accounts/store_kvdb.go @@ -257,6 +257,32 @@ func (s *BoltStore) UpsertAccountPayment(_ context.Context, id AccountID, return known, s.updateAccount(id, update) } +// DeleteAccountPayment removes a payment entry from the account with the given +// ID. It will return an error if the payment is not associated with the +// account. +// +// NOTE: This is part of the Store interface. +func (s *BoltStore) DeleteAccountPayment(_ context.Context, id AccountID, + hash lntypes.Hash) error { + + update := func(account *OffChainBalanceAccount) error { + // Check that this payment is actually associated with this + // account. + _, ok := account.Payments[hash] + if !ok { + return fmt.Errorf("payment with hash %s is not "+ + "associated with this account", hash) + } + + // Delete the payment and update the persisted account. + delete(account.Payments, hash) + + return nil + } + + return s.updateAccount(id, update) +} + func (s *BoltStore) updateAccount(id AccountID, updateFn func(*OffChainBalanceAccount) error) error { diff --git a/accounts/store_test.go b/accounts/store_test.go index fdae6345d..a7f35fcba 100644 --- a/accounts/store_test.go +++ b/accounts/store_test.go @@ -111,7 +111,7 @@ func TestAccountUpdateMethods(t *testing.T) { t.Parallel() ctx := context.Background() - t.Run("UpsertAccountPayment", func(t *testing.T) { + t.Run("Upsert and Delete AccountPayment", func(t *testing.T) { store := NewTestDB(t) acct, err := store.NewAccount(ctx, 1000, time.Time{}, "foo") @@ -258,6 +258,23 @@ func TestAccountUpdateMethods(t *testing.T) { FullAmount: 100, }, }) + + // Delete the first payment and make sure it is removed from the + // account. + err = store.DeleteAccountPayment(ctx, acct.ID, hash1) + require.NoError(t, err) + + assertBalanceAndPayments(400, AccountPayments{ + hash2: &PaymentEntry{ + Status: lnrpc.Payment_SUCCEEDED, + FullAmount: 100, + }, + }) + + // Test that deleting a payment that does not exist returns an + // error. + err = store.DeleteAccountPayment(ctx, acct.ID, hash1) + require.ErrorContains(t, err, "is not associated") }) }