Skip to content

Commit

Permalink
accounts: associate payments before sending them
Browse files Browse the repository at this point in the history
  • Loading branch information
ViktorTigerstrom committed Sep 28, 2023
1 parent 32b2a58 commit 666f19c
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 4 deletions.
13 changes: 13 additions & 0 deletions accounts/checkers.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ func checkSend(ctx context.Context, chainParams *chaincfg.Params,
}

// The invoice is optional.
var paymentHash lntypes.Hash
if len(invoice) > 0 {
payReq, err := zpay32.Decode(invoice, chainParams)
if err != nil {
Expand All @@ -531,6 +532,10 @@ func checkSend(ctx context.Context, chainParams *chaincfg.Params,
if payReq.MilliSat != nil && *payReq.MilliSat > sendAmt {
sendAmt = *payReq.MilliSat
}

if payReq.PaymentHash != nil {
paymentHash = *payReq.PaymentHash
}
}

// We also add the max fee to the amount to check. This might mean that
Expand All @@ -549,6 +554,14 @@ func checkSend(ctx context.Context, chainParams *chaincfg.Params,
return fmt.Errorf("error validating account balance: %w", err)
}

emptyHash := lntypes.Hash{}
if paymentHash != emptyHash {
err = service.AssociatePayment(acct.ID, paymentHash, sendAmt)
if err != nil {
return fmt.Errorf("error associating payment: %w", err)
}
}

return nil
}

Expand Down
6 changes: 6 additions & 0 deletions accounts/checkers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ func (m *mockService) AssociateInvoice(id AccountID, hash lntypes.Hash) error {
return nil
}

func (m *mockService) AssociatePayment(id AccountID, paymentHash lntypes.Hash,
amt lnwire.MilliSatoshi) error {

return nil
}

func (m *mockService) TrackPayment(_ AccountID, hash lntypes.Hash,
amt lnwire.MilliSatoshi) error {

Expand Down
7 changes: 5 additions & 2 deletions accounts/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ type Service interface {
// so we never need to debit the amount from the account.
RemovePayment(hash lntypes.Hash) error

// IsRunning returns true if the service can be used.
IsRunning() bool
// AssociatePayment associates a payment (hash) with the given account,
// ensuring that the payment will be tracked for a user when LiT is
// restarted.
AssociatePayment(id AccountID, paymentHash lntypes.Hash,
fullAmt lnwire.MilliSatoshi) error
}
58 changes: 56 additions & 2 deletions accounts/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,40 @@ func (s *InterceptorService) AssociateInvoice(id AccountID,
return s.store.UpdateAccount(account)
}

// AssociatePayment associates a payment (hash) with the given account,
// ensuring that the payment will be tracked for a user when LiT is
// restarted.
func (s *InterceptorService) AssociatePayment(id AccountID,
paymentHash lntypes.Hash, fullAmt lnwire.MilliSatoshi) error {

s.Lock()
defer s.Unlock()

account, err := s.store.Account(id)
if err != nil {
return err
}

// If the payment is already associated with the account, we don't need
// to associate it again.
_, ok := account.Payments[paymentHash]
if ok {
return nil
}

// Associate the payment with the account and store it.
account.Payments[paymentHash] = &PaymentEntry{
Status: lnrpc.Payment_UNKNOWN,
FullAmount: fullAmt,
}

if err := s.store.UpdateAccount(account); err != nil {
return fmt.Errorf("error updating account: %w", err)
}

return nil
}

// invoiceUpdate credits the account an invoice was registered with, in case the
// invoice was settled.
//
Expand Down Expand Up @@ -527,13 +561,33 @@ func (s *InterceptorService) TrackPayment(id AccountID, hash lntypes.Hash,
return nil
}

// Okay, we haven't tracked this payment before. So let's now associate
// the account with it.
account.Payments[hash] = &PaymentEntry{
Status: lnrpc.Payment_UNKNOWN,
FullAmount: fullAmt,
}

if err := s.store.UpdateAccount(account); err != nil {
if !ok {
// In the rare case that the payment isn't associated
// with an account yet, and we fail to update the
// account we will not be tracking the payment, even if
// track the service is restarted. Therefore the node
// runner needs to manually check if the payment was
// made and debit the account if that's the case.
errStr := "critical error: failed to store the " +
"payment with hash %v for user with account " +
"id %v. Manual intervention required! " +
"Verify if the payment was executed, and " +
"manually update the user account balance by " +
"subtracting the payment amount if it was"

mainChanErr := s.disableAndErrorfUnsafe(
errStr, hash, id,
)

s.mainErrCallback(mainChanErr)
}

return fmt.Errorf("error updating account: %w", err)
}

Expand Down

0 comments on commit 666f19c

Please sign in to comment.