Skip to content

Commit e10ae03

Browse files
committed
accounts: implement sqlc DebitAccount
This commit introduces the `DebitAccount` function in the sqlc store, to support decreasing the balance of an existing off-chain account by a specified amount for sql backends. We'll also add testing of the store functionality, as both stores now support the `DebitAccount` operation.
1 parent 845888a commit e10ae03

File tree

7 files changed

+96
-0
lines changed

7 files changed

+96
-0
lines changed

accounts/interface.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ type Store interface {
257257
CreditAccount(ctx context.Context, id AccountID,
258258
amount lnwire.MilliSatoshi) error
259259

260+
// DebitAccount decreases the balance of the account with the
261+
// given ID by the given amount.
262+
DebitAccount(ctx context.Context, id AccountID,
263+
amount lnwire.MilliSatoshi) error
264+
260265
// UpsertAccountPayment updates or inserts a payment entry for the given
261266
// account. Various functional options can be passed to modify the
262267
// behavior of the method. The returned boolean is true if the payment

accounts/store_kvdb.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,8 @@ func (s *BoltStore) CreditAccount(_ context.Context, id AccountID,
246246

247247
// DebitAccount decreases the balance of the account with the given ID
248248
// by the given amount.
249+
//
250+
// NOTE: This is part of the Store interface.
249251
func (s *BoltStore) DebitAccount(_ context.Context, id AccountID,
250252
amount lnwire.MilliSatoshi) error {
251253

accounts/store_sql.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const (
3434
//nolint:lll
3535
type SQLQueries interface {
3636
AddAccountInvoice(ctx context.Context, arg sqlc.AddAccountInvoiceParams) error
37+
DebitAccount(ctx context.Context, arg sqlc.DebitAccountParams) (int64, error)
3738
DeleteAccount(ctx context.Context, id int64) error
3839
DeleteAccountPayment(ctx context.Context, arg sqlc.DeleteAccountPaymentParams) error
3940
GetAccount(ctx context.Context, id int64) (sqlc.Account, error)
@@ -417,6 +418,38 @@ func (s *SQLStore) CreditAccount(ctx context.Context, alias AccountID,
417418
})
418419
}
419420

421+
// DebitAccount decreases the balance of the account with the given alias by
422+
// the given amount.
423+
//
424+
// NOTE: This is part of the Store interface.
425+
func (s *SQLStore) DebitAccount(ctx context.Context, alias AccountID,
426+
amount lnwire.MilliSatoshi) error {
427+
428+
var writeTxOpts db.QueriesTxOptions
429+
return s.db.ExecTx(ctx, &writeTxOpts, func(db SQLQueries) error {
430+
id, err := getAccountIDByAlias(ctx, db, alias)
431+
if err != nil {
432+
return err
433+
}
434+
435+
id, err = db.DebitAccount(
436+
ctx, sqlc.DebitAccountParams{
437+
ID: id,
438+
Amount: int64(amount),
439+
},
440+
)
441+
if errors.Is(err, sql.ErrNoRows) {
442+
return fmt.Errorf("cannot debit %v from the account "+
443+
"balance, as the resulting balance would be "+
444+
"below 0", int64(amount/1000))
445+
} else if err != nil {
446+
return err
447+
}
448+
449+
return s.markAccountUpdated(ctx, db, id)
450+
})
451+
}
452+
420453
// Account retrieves an account from the SQL store and un-marshals it. If the
421454
// account cannot be found, then ErrAccNotFound is returned.
422455
//

accounts/store_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package accounts
22

33
import (
44
"context"
5+
"github.com/lightningnetwork/lnd/lnwire"
56
"testing"
67
"time"
78

@@ -71,6 +72,14 @@ func TestAccountStore(t *testing.T) {
7172
)
7273
require.NoError(t, err)
7374

75+
// Adjust the account balance by first crediting 10000, and then
76+
// debiting 5000.
77+
err = store.CreditAccount(ctx, acct1.ID, lnwire.MilliSatoshi(10000))
78+
require.NoError(t, err)
79+
80+
err = store.DebitAccount(ctx, acct1.ID, lnwire.MilliSatoshi(5000))
81+
require.NoError(t, err)
82+
7483
// Update the in-memory account so that we can compare it with the
7584
// account we get from the store.
7685
acct1.CurrentBalance = -500
@@ -85,11 +94,30 @@ func TestAccountStore(t *testing.T) {
8594
}
8695
acct1.Invoices[lntypes.Hash{12, 34, 56, 78}] = struct{}{}
8796
acct1.Invoices[lntypes.Hash{34, 56, 78, 90}] = struct{}{}
97+
acct1.CurrentBalance += 10000
98+
acct1.CurrentBalance -= 5000
8899

89100
dbAccount, err = store.Account(ctx, acct1.ID)
90101
require.NoError(t, err)
91102
assertEqualAccounts(t, acct1, dbAccount)
92103

104+
// Test that adjusting the balance to exactly 0 should work, while
105+
// adjusting the balance to below 0 should fail.
106+
err = store.DebitAccount(
107+
ctx, acct1.ID, lnwire.MilliSatoshi(acct1.CurrentBalance),
108+
)
109+
require.NoError(t, err)
110+
111+
acct1.CurrentBalance = 0
112+
113+
dbAccount, err = store.Account(ctx, acct1.ID)
114+
require.NoError(t, err)
115+
assertEqualAccounts(t, acct1, dbAccount)
116+
117+
// Adjusting the value to below 0 should fail.
118+
err = store.DebitAccount(ctx, acct1.ID, lnwire.MilliSatoshi(1))
119+
require.ErrorContains(t, err, "balance would be below 0")
120+
93121
// Sleep just a tiny bit to make sure we are never too quick to measure
94122
// the expiry, even though the time is nanosecond scale and writing to
95123
// the store and reading again should take at least a couple of

db/sqlc/accounts.sql.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

db/sqlc/querier.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

db/sqlc/queries/accounts.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ SET current_balance_msat = $1
99
WHERE id = $2
1010
RETURNING id;
1111

12+
-- name: DebitAccount :one
13+
UPDATE accounts
14+
SET current_balance_msat = current_balance_msat - sqlc.arg(amount)
15+
WHERE id = sqlc.arg(id)
16+
AND current_balance_msat >= sqlc.arg(amount)
17+
RETURNING id;
18+
1219
-- name: UpdateAccountExpiry :one
1320
UPDATE accounts
1421
SET expiration = $1

0 commit comments

Comments
 (0)