diff --git a/channeldb/payment_control.go b/channeldb/payment_control.go index b5c57a9fe..2f22d4c77 100644 --- a/channeldb/payment_control.go +++ b/channeldb/payment_control.go @@ -26,6 +26,10 @@ var ( // already "in flight" on the network. ErrPaymentInFlight = errors.New("payment is in transition") + // ErrPaymentExists is returned when we try to initialize an already + // existing payment that is not failed. + ErrPaymentExists = errors.New("payment already exists") + // ErrPaymentNotInitiated is returned if the payment wasn't initiated. ErrPaymentNotInitiated = errors.New("payment isn't initiated") @@ -148,10 +152,15 @@ func (p *PaymentControl) InitPayment(paymentHash lntypes.Hash, // retrying the payment or return a specific error. case err == nil: switch paymentStatus { - // We allow retrying failed payments. case StatusFailed: + // We already have payment creation info for this + // payment so we won't allow creating a duplicate one. + case StatusInitiated: + updateErr = ErrPaymentExists + return nil + // We already have an InFlight payment on the network. // We will disallow any new payments. case StatusInFlight: @@ -727,6 +736,9 @@ func ensureInFlight(payment *MPPayment) error { paymentStatus := payment.Status switch { + // Newly created payment is also inflight. + case paymentStatus == StatusInitiated: + return nil // The payment was indeed InFlight. case paymentStatus == StatusInFlight: diff --git a/channeldb/payment_status.go b/channeldb/payment_status.go index f4b69c42c..38ef5a047 100644 --- a/channeldb/payment_status.go +++ b/channeldb/payment_status.go @@ -7,22 +7,29 @@ const ( // NOTE: PaymentStatus = 0 was previously used for status unknown and // is now deprecated. + // StatusInitiated is the status where a payment has just been + // initiated. + StatusInitiated PaymentStatus = 1 + // StatusInFlight is the status where a payment has been initiated, but // a response has not been received. - StatusInFlight PaymentStatus = 1 + StatusInFlight PaymentStatus = 2 // StatusSucceeded is the status where a payment has been initiated and // the payment was completed successfully. - StatusSucceeded PaymentStatus = 2 + StatusSucceeded PaymentStatus = 3 // StatusFailed is the status where a payment has been initiated and a // failure result has come back. - StatusFailed PaymentStatus = 3 + StatusFailed PaymentStatus = 4 ) // String returns readable representation of payment status. func (ps PaymentStatus) String() string { switch ps { + case StatusInitiated: + return "Initiated" + case StatusInFlight: return "In Flight" diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index ccdaa5c58..683e5663d 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -446,10 +446,17 @@ func (s *Server) SendToRouteV2(ctx context.Context, } // Transform user errors to grpc code. - if err == channeldb.ErrPaymentInFlight || - err == channeldb.ErrAlreadyPaid { + switch { + case errors.Is(err, channeldb.ErrPaymentExists): + fallthrough - return nil, status.Error(codes.AlreadyExists, err.Error()) + case errors.Is(err, channeldb.ErrPaymentInFlight): + fallthrough + + case errors.Is(err, channeldb.ErrAlreadyPaid): + return nil, status.Error( + codes.AlreadyExists, err.Error(), + ) } return nil, err diff --git a/routing/router.go b/routing/router.go index 48c4a9d26..4765257ef 100644 --- a/routing/router.go +++ b/routing/router.go @@ -2276,7 +2276,8 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route, switch { // If this is an MPP attempt and the hash is already registered with // the database, we can go on to launch the shard. - case err == channeldb.ErrPaymentInFlight && mpp != nil: + case mpp != nil && errors.Is(err, channeldb.ErrPaymentInFlight): + case mpp != nil && errors.Is(err, channeldb.ErrPaymentExists): // Any other error is not tolerated. case err != nil: