paymentsdb: implement SettleAttempt for sql backend
What changed, and why it matters
This commit adds a missing database method called SettleAttempt to LND's new SQL-based payments storage backend. It records when a Lightning payment attempt succeeds, including the cryptographic proof (preimage) and the settlement time. There is no indication in the commit that this fixes a security vulnerability; it appears to be ordinary feature completion work.
No immediate security action required. Review as part of normal code quality assurance for the SQL backend migration.
Security signals we found
No security-relevant keywords in commit title or message
No CVE, advisory, or security-fix references in commit
Code follows existing transactional patterns and status checks
Preimage handling uses standard settleInfo.Preimage[:] slice conversion
Evidence from the diff
The patch implements SettleAttempt for the SQL payments store (payments/db/sql_store.go). It wraps the operation in a write transaction, fetches the payment, verifies the payment status is updatable, calls the generated SettleAttempt query with the preimage and resolution metadata, and returns the fully populated MPPayment. The implementation mirrors the existing RegisterAttempt/FailAttempt patterns and uses the same SQLQueries/ExecTx abstraction. No security-sensitive deviations are visible in the diff.
Changed components
payments/db/sql_store.goLND SQL payments database backendInspect captured patch +63 / −0
diff --git a/payments/db/sql_store.go b/payments/db/sql_store.go
index 83f6943..8030a86 100644
--- a/payments/db/sql_store.go
+++ b/payments/db/sql_store.go
@@ -1290,3 +1290,66 @@ func (s *SQLStore) RegisterAttempt(paymentHash lntypes.Hash,
return mpPayment, nil
}
+
+// SettleAttempt marks the specified HTLC attempt as successfully settled,
+// recording the payment preimage and settlement time. The preimage serves as
+// cryptographic proof of payment and is atomically saved to the database.
+//
+// This method is part of the PaymentControl interface, which is embedded in
+// the PaymentWriter interface and ultimately the DB interface. It represents
+// step 3a in the payment lifecycle control flow (step 3b is FailAttempt),
+// called after RegisterAttempt when an HTLC successfully completes.
+func (s *SQLStore) SettleAttempt(paymentHash lntypes.Hash,
+ attemptID uint64, settleInfo *HTLCSettleInfo) (*MPPayment, error) {
+
+ ctx := context.TODO()
+
+ var mpPayment *MPPayment
+
+ err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
+ dbPayment, err := db.FetchPayment(ctx, paymentHash[:])
+ if err != nil {
+ return fmt.Errorf("failed to fetch payment: %w", err)
+ }
+
+ paymentStatus, err := computePaymentStatusFromDB(
+ ctx, db, dbPayment,
+ )
+ if err != nil {
+ return fmt.Errorf("failed to compute payment "+
+ "status: %w", err)
+ }
+
+ if err := paymentStatus.updatable(); err != nil {
+ return fmt.Errorf("payment is not updatable: %w", err)
+ }
+
+ err = db.SettleAttempt(ctx, sqlc.SettleAttemptParams{
+ AttemptIndex: int64(attemptID),
+ ResolutionTime: time.Now(),
+ ResolutionType: int32(HTLCAttemptResolutionSettled),
+ SettlePreimage: settleInfo.Preimage[:],
+ })
+ if err != nil {
+ return fmt.Errorf("failed to settle attempt: %w", err)
+ }
+
+ // Fetch the complete payment after we settled the attempt.
+ mpPayment, err = s.fetchPaymentWithCompleteData(
+ ctx, db, dbPayment,
+ )
+ if err != nil {
+ return fmt.Errorf("failed to fetch payment with "+
+ "complete data: %w", err)
+ }
+
+ return nil
+ }, func() {
+ mpPayment = nil
+ })
+ if err != nil {
+ return nil, fmt.Errorf("failed to settle attempt: %w", err)
+ }
+
+ return mpPayment, nil
+}
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.