multi: implement Fail method for sql backend

This commit is contained in:
ziggie committed 2026-02-25 08:02:15 +01:00
1 parent caf73c59da
commit fd6796e561
4 files changed
+83

No files matched your search

+66
View File
@@ -77,6 +77,8 @@ type SQLQueries interface {
SettleAttempt(ctx context.Context, arg sqlc.SettleAttemptParams) error
FailPayment(ctx context.Context, arg sqlc.FailPaymentParams) (sql.Result, error)
DeletePayment(ctx context.Context, paymentID int64) error
// DeleteFailedAttempts removes all failed HTLCs from the db for a
@@ -1360,3 +1362,67 @@ func (s *SQLStore) SettleAttempt(paymentHash lntypes.Hash,
return mpPayment, nil
}
// Fail records the ultimate reason why a payment failed. This method stores
// the failure reason for record keeping but does not enforce that all HTLC
// attempts are resolved - HTLCs may still be in flight when this is called.
//
// The payment's actual status transition to StatusFailed is determined by the
// payment state calculation, which considers both the recorded failure reason
// and the current state of all HTLC attempts. The status will transition to
// StatusFailed once all HTLCs are resolved and/or a failure reason is recorded.
//
// NOTE: According to the interface contract, this should only be called when
// all active attempts are already failed. However, the implementation allows
// concurrent calls and does not validate this precondition, enabling the last
// failing attempt to record the failure reason without synchronization.
//
// This method is part of the PaymentControl interface, which is embedded in
// the PaymentWriter interface and ultimately the DB interface. It represents
// step 4 in the payment lifecycle control flow.
func (s *SQLStore) Fail(paymentHash lntypes.Hash,
reason FailureReason) (*MPPayment, error) {
ctx := context.TODO()
var mpPayment *MPPayment
err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
result, err := db.FailPayment(ctx, sqlc.FailPaymentParams{
PaymentIdentifier: paymentHash[:],
FailReason: sqldb.SQLInt32(reason),
})
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrPaymentNotInitiated
}
payment, err := db.FetchPayment(ctx, paymentHash[:])
if err != nil {
return fmt.Errorf("failed to fetch payment: %w", err)
}
mpPayment, err = fetchPaymentWithCompleteData(
ctx, s.cfg.QueryCfg, db, payment,
)
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 fail payment: %w", err)
}
return mpPayment, nil
}
+13
View File
@@ -85,6 +85,19 @@ func (q *Queries) FailAttempt(ctx context.Context, arg FailAttemptParams) error
return err
}
const failPayment = `-- name: FailPayment :execresult
UPDATE payments SET fail_reason = $1 WHERE payment_identifier = $2
`
type FailPaymentParams struct {
FailReason sql.NullInt32
PaymentIdentifier []byte
}
func (q *Queries) FailPayment(ctx context.Context, arg FailPaymentParams) (sql.Result, error) {
return q.db.ExecContext(ctx, failPayment, arg.FailReason, arg.PaymentIdentifier)
}
const fetchAllInflightAttempts = `-- name: FetchAllInflightAttempts :many
SELECT
ha.id,
+1
View File
@@ -35,6 +35,7 @@ type Querier interface {
DeleteUnconnectedNodes(ctx context.Context) ([][]byte, error)
DeleteZombieChannel(ctx context.Context, arg DeleteZombieChannelParams) (sql.Result, error)
FailAttempt(ctx context.Context, arg FailAttemptParams) error
FailPayment(ctx context.Context, arg FailPaymentParams) (sql.Result, error)
FetchAMPSubInvoiceHTLCs(ctx context.Context, arg FetchAMPSubInvoiceHTLCsParams) ([]FetchAMPSubInvoiceHTLCsRow, error)
FetchAMPSubInvoices(ctx context.Context, arg FetchAMPSubInvoicesParams) ([]AmpSubInvoice, error)
// Fetch all inflight attempts across all payments
+3
View File
@@ -352,3 +352,6 @@ VALUES (
@htlc_fail_reason,
@failure_msg
);
-- name: FailPayment :execresult
UPDATE payments SET fail_reason = $1 WHERE payment_identifier = $2;