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 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 DeletePayment(ctx context.Context, paymentID int64) error
// DeleteFailedAttempts removes all failed HTLCs from the db for a // DeleteFailedAttempts removes all failed HTLCs from the db for a
@@ -1360,3 +1362,67 @@ func (s *SQLStore) SettleAttempt(paymentHash lntypes.Hash,
return mpPayment, nil 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 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 const fetchAllInflightAttempts = `-- name: FetchAllInflightAttempts :many
SELECT SELECT
ha.id, ha.id,
+1
View File
@@ -35,6 +35,7 @@ type Querier interface {
DeleteUnconnectedNodes(ctx context.Context) ([][]byte, error) DeleteUnconnectedNodes(ctx context.Context) ([][]byte, error)
DeleteZombieChannel(ctx context.Context, arg DeleteZombieChannelParams) (sql.Result, error) DeleteZombieChannel(ctx context.Context, arg DeleteZombieChannelParams) (sql.Result, error)
FailAttempt(ctx context.Context, arg FailAttemptParams) 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) FetchAMPSubInvoiceHTLCs(ctx context.Context, arg FetchAMPSubInvoiceHTLCsParams) ([]FetchAMPSubInvoiceHTLCsRow, error)
FetchAMPSubInvoices(ctx context.Context, arg FetchAMPSubInvoicesParams) ([]AmpSubInvoice, error) FetchAMPSubInvoices(ctx context.Context, arg FetchAMPSubInvoicesParams) ([]AmpSubInvoice, error)
// Fetch all inflight attempts across all payments // Fetch all inflight attempts across all payments
+3
View File
@@ -352,3 +352,6 @@ VALUES (
@htlc_fail_reason, @htlc_fail_reason,
@failure_msg @failure_msg
); );
-- name: FailPayment :execresult
UPDATE payments SET fail_reason = $1 WHERE payment_identifier = $2;