Skip to content

Commit 5a678b8

Browse files
committed
accounts: associate payments before sending them
1 parent 747e299 commit 5a678b8

File tree

4 files changed

+79
-4
lines changed

4 files changed

+79
-4
lines changed

accounts/checkers.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,7 @@ func checkSend(ctx context.Context, chainParams *chaincfg.Params,
522522
}
523523

524524
// The invoice is optional.
525+
var paymentHash lntypes.Hash
525526
if len(invoice) > 0 {
526527
payReq, err := zpay32.Decode(invoice, chainParams)
527528
if err != nil {
@@ -531,6 +532,10 @@ func checkSend(ctx context.Context, chainParams *chaincfg.Params,
531532
if payReq.MilliSat != nil && *payReq.MilliSat > sendAmt {
532533
sendAmt = *payReq.MilliSat
533534
}
535+
536+
if payReq.PaymentHash != nil {
537+
paymentHash = *payReq.PaymentHash
538+
}
534539
}
535540

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

557+
emptyHash := lntypes.Hash{}
558+
if paymentHash != emptyHash {
559+
err = service.AssociatePayment(acct.ID, paymentHash, sendAmt)
560+
if err != nil {
561+
return fmt.Errorf("error associating payment: %w", err)
562+
}
563+
}
564+
552565
return nil
553566
}
554567

accounts/checkers_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ func (m *mockService) AssociateInvoice(id AccountID, hash lntypes.Hash) error {
6868
return nil
6969
}
7070

71+
func (m *mockService) AssociatePayment(id AccountID, paymentHash lntypes.Hash,
72+
amt lnwire.MilliSatoshi) error {
73+
74+
return nil
75+
}
76+
7177
func (m *mockService) TrackPayment(_ AccountID, hash lntypes.Hash,
7278
amt lnwire.MilliSatoshi) error {
7379

accounts/interface.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ type Service interface {
247247
// so we never need to debit the amount from the account.
248248
RemovePayment(hash lntypes.Hash) error
249249

250-
// IsRunning returns true if the service can be used.
251-
IsRunning() bool
250+
// AssociatePayment associates a payment (hash) with the given account,
251+
// ensuring that the payment will be tracked for a user when LiT is
252+
// restarted.
253+
AssociatePayment(id AccountID, paymentHash lntypes.Hash,
254+
fullAmt lnwire.MilliSatoshi) error
252255
}

accounts/service.go

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,39 @@ func (s *InterceptorService) AssociateInvoice(id AccountID,
419419
return s.store.UpdateAccount(account)
420420
}
421421

422+
// AssociatePayment associates a payment (hash) with the given account,
423+
// ensuring that the payment will be tracked for a user when LiT is
424+
// restarted.
425+
func (s *InterceptorService) AssociatePayment(id AccountID,
426+
paymentHash lntypes.Hash, fullAmt lnwire.MilliSatoshi) error {
427+
428+
s.Lock()
429+
defer s.Unlock()
430+
431+
account, err := s.store.Account(id)
432+
if err != nil {
433+
return err
434+
}
435+
436+
// If the payment is already associated with the account, we don't need
437+
// to associate it again.
438+
_, ok := account.Payments[paymentHash]
439+
if ok {
440+
return nil
441+
}
442+
443+
// Associate the payment with the account and store it.
444+
account.Payments[paymentHash] = &PaymentEntry{
445+
Status: lnrpc.Payment_UNKNOWN,
446+
FullAmount: fullAmt,
447+
}
448+
if err := s.store.UpdateAccount(account); err != nil {
449+
return fmt.Errorf("error updating account: %w", err)
450+
}
451+
452+
return nil
453+
}
454+
422455
// invoiceUpdate credits the account an invoice was registered with, in case the
423456
// invoice was settled.
424457
//
@@ -531,13 +564,33 @@ func (s *InterceptorService) TrackPayment(id AccountID, hash lntypes.Hash,
531564
return nil
532565
}
533566

534-
// Okay, we haven't tracked this payment before. So let's now associate
535-
// the account with it.
536567
account.Payments[hash] = &PaymentEntry{
537568
Status: lnrpc.Payment_UNKNOWN,
538569
FullAmount: fullAmt,
539570
}
571+
540572
if err := s.store.UpdateAccount(account); err != nil {
573+
if !ok {
574+
// In the rare case that the payment isn't associated
575+
// with an account yet, and we fail to update the
576+
// account we will not be tracking the payment, even if
577+
// track the service is restarted. Therefore the node
578+
// runner needs to manually check if the payment was
579+
// made and debit the account if that's the case.
580+
errStr := "critical error: failed to store the " +
581+
"payment with hash %v for user with account " +
582+
"id %v. Manual intervention required! " +
583+
"Verify if the payment was executed, and " +
584+
"manually update the user account balance by " +
585+
"subtracting the payment amount if it was"
586+
587+
mainChanErr := s.disableAndErrorfUnsafe(
588+
errStr, hash, id,
589+
)
590+
591+
s.mainErrCallback(mainChanErr)
592+
}
593+
541594
return fmt.Errorf("error updating account: %w", err)
542595
}
543596

0 commit comments

Comments
 (0)