accounts: implement CreditAccount & DebitAccount

This commit implements the `CreditAccount` and `DebitAccount` endpoints
for the accounts subsystem.
This commit is contained in:
Viktor Tigerström 2025-02-11 19:14:36 +01:00
parent 3e8c6d728d
commit b2ec5974df
No known key found for this signature in database
GPG key ID: B984570980684DCC
4 changed files with 149 additions and 0 deletions

View file

@ -63,6 +63,18 @@ type mockService struct {
*requestValuesStore
}
func (m *mockService) CreditAccount(_ context.Context, _ AccountID,
_ lnwire.MilliSatoshi) (*OffChainBalanceAccount, error) {
return nil, nil
}
func (m *mockService) DebitAccount(_ context.Context, _ AccountID,
_ lnwire.MilliSatoshi) (*OffChainBalanceAccount, error) {
return nil, nil
}
func newMockService() *mockService {
return &mockService{
acctBalanceMsat: 0,

View file

@ -331,6 +331,16 @@ type Service interface {
PaymentErrored(ctx context.Context, id AccountID,
hash lntypes.Hash) error
// CreditAccount increases the balance of an existing account in the
// database.
CreditAccount(ctx context.Context, accountID AccountID,
amount lnwire.MilliSatoshi) (*OffChainBalanceAccount, error)
// DebitAccount decreases the balance of an existing account in the
// database.
DebitAccount(ctx context.Context, accountID AccountID,
amount lnwire.MilliSatoshi) (*OffChainBalanceAccount, error)
RequestValuesStore
}

View file

@ -130,6 +130,83 @@ func (s *RPCServer) UpdateAccount(ctx context.Context,
return marshalAccount(account), nil
}
// CreditAccount increases the balance of an existing account in the account
// database, by the given amount.
func (s *RPCServer) CreditAccount(ctx context.Context,
req *litrpc.CreditAccountRequest) (*litrpc.CreditAccountResponse,
error) {
if req.GetAccount() == nil {
return nil, fmt.Errorf("account param must be specified")
}
var id, label string
switch idType := req.Account.Identifier.(type) {
case *litrpc.AccountIdentifier_Id:
id = idType.Id
case *litrpc.AccountIdentifier_Label:
label = idType.Label
}
log.Infof("[creditaccount] id=%s, label=%v, amount=%d", id, label,
req.Amount)
amount := lnwire.MilliSatoshi(req.Amount * 1000)
accountID, err := s.findAccount(ctx, id, label)
if err != nil {
return nil, err
}
account, err := s.service.CreditAccount(ctx, accountID, amount)
if err != nil {
return nil, err
}
return &litrpc.CreditAccountResponse{
Account: marshalAccount(account),
}, nil
}
// DebitAccount decreases the balance of an existing account in the account
// database, by the given amount.
func (s *RPCServer) DebitAccount(ctx context.Context,
req *litrpc.DebitAccountRequest) (*litrpc.DebitAccountResponse, error) {
if req.GetAccount() == nil {
return nil, fmt.Errorf("account param must be specified")
}
var id, label string
switch idType := req.Account.Identifier.(type) {
case *litrpc.AccountIdentifier_Id:
id = idType.Id
case *litrpc.AccountIdentifier_Label:
label = idType.Label
}
log.Infof("[debitaccount] id=%s, label=%v, amount=%d", id, label,
req.Amount)
amount := lnwire.MilliSatoshi(req.Amount * 1000)
accountID, err := s.findAccount(ctx, id, label)
if err != nil {
return nil, err
}
account, err := s.service.DebitAccount(ctx, accountID, amount)
if err != nil {
return nil, err
}
return &litrpc.DebitAccountResponse{
Account: marshalAccount(account),
}, nil
}
// ListAccounts returns all accounts that are currently stored in the account
// database.
func (s *RPCServer) ListAccounts(ctx context.Context,

View file

@ -345,6 +345,56 @@ func (s *InterceptorService) UpdateAccount(ctx context.Context,
return s.store.Account(ctx, accountID)
}
// CreditAccount increases the balance of an existing account in the database.
func (s *InterceptorService) CreditAccount(ctx context.Context,
accountID AccountID,
amount lnwire.MilliSatoshi) (*OffChainBalanceAccount, error) {
s.Lock()
defer s.Unlock()
// As this function updates account balances, we require that the
// service is running before we execute it.
if !s.isRunningUnsafe() {
// This case can only happen if the service is disabled while
// we're processing a request.
return nil, ErrAccountServiceDisabled
}
// Credit the account in the db.
err := s.store.CreditAccount(ctx, accountID, amount)
if err != nil {
return nil, fmt.Errorf("unable to credit account: %w", err)
}
return s.store.Account(ctx, accountID)
}
// DebitAccount decreases the balance of an existing account in the database.
func (s *InterceptorService) DebitAccount(ctx context.Context,
accountID AccountID,
amount lnwire.MilliSatoshi) (*OffChainBalanceAccount, error) {
s.Lock()
defer s.Unlock()
// As this function updates account balances, we require that the
// service is running before we execute it.
if !s.isRunningUnsafe() {
// This case can only happen if the service is disabled while
// we're processing a request.
return nil, ErrAccountServiceDisabled
}
// Debit the account in the db.
err := s.store.DebitAccount(ctx, accountID, amount)
if err != nil {
return nil, fmt.Errorf("unable to debit account: %w", err)
}
return s.store.Account(ctx, accountID)
}
// Account retrieves an account from the bolt DB and un-marshals it. If the
// account cannot be found, then ErrAccNotFound is returned.
func (s *InterceptorService) Account(ctx context.Context,