• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 17027244024

17 Aug 2025 11:32PM UTC coverage: 57.287% (-9.5%) from 66.765%
17027244024

Pull #10167

github

web-flow
Merge fcb4f4303 into fb1adfc21
Pull Request #10167: multi: bump Go to 1.24.6

3 of 18 new or added lines in 6 files covered. (16.67%)

28537 existing lines in 457 files now uncovered.

99094 of 172978 relevant lines covered (57.29%)

1.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.26
/payments/db/payment_status.go
1
package paymentsdb
2

3
import (
4
        "fmt"
5
)
6

7
// PaymentStatus represent current status of payment.
8
type PaymentStatus byte
9

10
const (
11
        // NOTE: PaymentStatus = 0 was previously used for status unknown and
12
        // is now deprecated.
13

14
        // StatusInitiated is the status where a payment has just been
15
        // initiated.
16
        StatusInitiated PaymentStatus = 1
17

18
        // StatusInFlight is the status where a payment has been initiated, but
19
        // a response has not been received.
20
        StatusInFlight PaymentStatus = 2
21

22
        // StatusSucceeded is the status where a payment has been initiated and
23
        // the payment was completed successfully.
24
        StatusSucceeded PaymentStatus = 3
25

26
        // StatusFailed is the status where a payment has been initiated and a
27
        // failure result has come back.
28
        StatusFailed PaymentStatus = 4
29
)
30

31
// errPaymentStatusUnknown is returned when a payment has an unknown status.
32
var errPaymentStatusUnknown = fmt.Errorf("unknown payment status")
33

34
// String returns readable representation of payment status.
35
func (ps PaymentStatus) String() string {
3✔
36
        switch ps {
3✔
37
        case StatusInitiated:
3✔
38
                return "Initiated"
3✔
39

40
        case StatusInFlight:
3✔
41
                return "In Flight"
3✔
42

43
        case StatusSucceeded:
3✔
44
                return "Succeeded"
3✔
45

46
        case StatusFailed:
3✔
47
                return "Failed"
3✔
48

UNCOV
49
        default:
×
UNCOV
50
                return "Unknown"
×
51
        }
52
}
53

54
// initializable returns an error to specify whether initiating the payment
55
// with its current status is allowed. A payment can only be initialized if it
56
// hasn't been created yet or already failed.
57
func (ps PaymentStatus) initializable() error {
3✔
58
        switch ps {
3✔
59
        // The payment has been created already. We will disallow creating it
60
        // again in case other goroutines have already been creating HTLCs for
61
        // it.
UNCOV
62
        case StatusInitiated:
×
UNCOV
63
                return ErrPaymentExists
×
64

65
        // We already have an InFlight payment on the network. We will disallow
66
        // any new payments.
67
        case StatusInFlight:
3✔
68
                return ErrPaymentInFlight
3✔
69

70
        // The payment has been attempted and is succeeded so we won't allow
71
        // creating it again.
UNCOV
72
        case StatusSucceeded:
×
UNCOV
73
                return ErrAlreadyPaid
×
74

75
        // We allow retrying failed payments.
76
        case StatusFailed:
3✔
77
                return nil
3✔
78

UNCOV
79
        default:
×
UNCOV
80
                return fmt.Errorf("%w: %v", ErrUnknownPaymentStatus,
×
UNCOV
81
                        ps)
×
82
        }
83
}
84

85
// removable returns an error to specify whether deleting the payment with its
86
// current status is allowed. A payment cannot be safely deleted if it has
87
// inflight HTLCs.
88
func (ps PaymentStatus) removable() error {
3✔
89
        switch ps {
3✔
90
        // The payment has been created but has no HTLCs and can be removed.
UNCOV
91
        case StatusInitiated:
×
UNCOV
92
                return nil
×
93

94
        // There are still inflight HTLCs and the payment needs to wait for the
95
        // final outcomes.
UNCOV
96
        case StatusInFlight:
×
UNCOV
97
                return ErrPaymentInFlight
×
98

99
        // The payment has been attempted and is succeeded and is allowed to be
100
        // removed.
101
        case StatusSucceeded:
3✔
102
                return nil
3✔
103

104
        // Failed payments are allowed to be removed.
UNCOV
105
        case StatusFailed:
×
UNCOV
106
                return nil
×
107

UNCOV
108
        default:
×
UNCOV
109
                return fmt.Errorf("%w: %v", ErrUnknownPaymentStatus,
×
UNCOV
110
                        ps)
×
111
        }
112
}
113

114
// updatable returns an error to specify whether the payment's HTLCs can be
115
// updated. A payment can update its HTLCs when it has inflight HTLCs.
116
func (ps PaymentStatus) updatable() error {
3✔
117
        switch ps {
3✔
118
        // Newly created payments can be updated.
119
        case StatusInitiated:
3✔
120
                return nil
3✔
121

122
        // Inflight payments can be updated.
123
        case StatusInFlight:
3✔
124
                return nil
3✔
125

126
        // If the payment has a terminal condition, we won't allow any updates.
127
        case StatusSucceeded:
3✔
128
                return ErrPaymentAlreadySucceeded
3✔
129

130
        case StatusFailed:
3✔
131
                return ErrPaymentAlreadyFailed
3✔
132

UNCOV
133
        default:
×
UNCOV
134
                return fmt.Errorf("%w: %v", ErrUnknownPaymentStatus,
×
UNCOV
135
                        ps)
×
136
        }
137
}
138

139
// decidePaymentStatus uses the payment's DB state to determine a memory status
140
// that's used by the payment router to decide following actions.
141
// Together, we use four variables to determine the payment's status,
142
//   - inflight: whether there are any pending HTLCs.
143
//   - settled: whether any of the HTLCs has been settled.
144
//   - htlc failed: whether any of the HTLCs has been failed.
145
//   - payment failed: whether the payment has been marked as failed.
146
//
147
// Based on the above variables, we derive the status using the following
148
// table,
149
// | inflight | settled | htlc failed | payment failed |         status       |
150
// |:--------:|:-------:|:-----------:|:--------------:|:--------------------:|
151
// |   true   |   true  |     true    |      true      |    StatusInFlight    |
152
// |   true   |   true  |     true    |      false     |    StatusInFlight    |
153
// |   true   |   true  |     false   |      true      |    StatusInFlight    |
154
// |   true   |   true  |     false   |      false     |    StatusInFlight    |
155
// |   true   |   false |     true    |      true      |    StatusInFlight    |
156
// |   true   |   false |     true    |      false     |    StatusInFlight    |
157
// |   true   |   false |     false   |      true      |    StatusInFlight    |
158
// |   true   |   false |     false   |      false     |    StatusInFlight    |
159
// |   false  |   true  |     true    |      true      |    StatusSucceeded   |
160
// |   false  |   true  |     true    |      false     |    StatusSucceeded   |
161
// |   false  |   true  |     false   |      true      |    StatusSucceeded   |
162
// |   false  |   true  |     false   |      false     |    StatusSucceeded   |
163
// |   false  |   false |     true    |      true      |      StatusFailed    |
164
// |   false  |   false |     true    |      false     |    StatusInFlight    |
165
// |   false  |   false |     false   |      true      |      StatusFailed    |
166
// |   false  |   false |     false   |      false     |    StatusInitiated   |
167
//
168
// When `inflight`, `settled`, `htlc failed`, and `payment failed` are false,
169
// this indicates the payment is newly created and hasn't made any HTLCs yet.
170
// When `inflight` and `settled` are false, `htlc failed` is true yet `payment
171
// failed` is false, this indicates all the payment's HTLCs have occurred a
172
// temporarily failure and the payment is still in-flight.
173
func decidePaymentStatus(htlcs []HTLCAttempt,
174
        reason *FailureReason) (PaymentStatus, error) {
3✔
175

3✔
176
        var (
3✔
177
                inflight      bool
3✔
178
                htlcSettled   bool
3✔
179
                htlcFailed    bool
3✔
180
                paymentFailed bool
3✔
181
        )
3✔
182

3✔
183
        // If we have a failure reason, the payment is failed.
3✔
184
        if reason != nil {
6✔
185
                paymentFailed = true
3✔
186
        }
3✔
187

188
        // Go through all HTLCs for this payment, check whether we have any
189
        // settled HTLC, and any still in-flight.
190
        for _, h := range htlcs {
6✔
191
                if h.Failure != nil {
6✔
192
                        htlcFailed = true
3✔
193
                        continue
3✔
194
                }
195

196
                if h.Settle != nil {
6✔
197
                        htlcSettled = true
3✔
198
                        continue
3✔
199
                }
200

201
                // If any of the HTLCs are not failed nor settled, we
202
                // still have inflight HTLCs.
203
                inflight = true
3✔
204
        }
205

206
        // Use the DB state to determine the status of the payment.
207
        switch {
3✔
208
        // If we have inflight HTLCs, no matter we have settled or failed
209
        // HTLCs, or the payment failed, we still consider it inflight so we
210
        // inform upper systems to wait for the results.
211
        case inflight:
3✔
212
                return StatusInFlight, nil
3✔
213

214
        // If we have no in-flight HTLCs, and at least one of the HTLCs is
215
        // settled, the payment succeeded.
216
        //
217
        // NOTE: when reaching this case, paymentFailed could be true, which
218
        // means we have a conflicting state for this payment. We choose to
219
        // mark the payment as succeeded because it's the receiver's
220
        // responsibility to only settle the payment iff all HTLCs are
221
        // received.
222
        case htlcSettled:
3✔
223
                return StatusSucceeded, nil
3✔
224

225
        // If we have no in-flight HTLCs, and the payment failure is set, the
226
        // payment is considered failed.
227
        //
228
        // NOTE: when reaching this case, settled must be false.
229
        case paymentFailed:
3✔
230
                return StatusFailed, nil
3✔
231

232
        // If we have no in-flight HTLCs, yet the payment is NOT failed, it
233
        // means all the HTLCs are failed. In this case we can attempt more
234
        // HTLCs.
235
        //
236
        // NOTE: when reaching this case, both settled and paymentFailed must
237
        // be false.
238
        case htlcFailed:
3✔
239
                return StatusInFlight, nil
3✔
240

241
        // If none of the HTLCs is either settled or failed, and we have no
242
        // inflight HTLCs, this means the payment has no HTLCs created yet.
243
        //
244
        // NOTE: when reaching this case, both settled and paymentFailed must
245
        // be false.
246
        case !htlcFailed:
3✔
247
                return StatusInitiated, nil
3✔
248

249
        // Otherwise an impossible state is reached.
250
        //
251
        // NOTE: we should never end up here.
252
        default:
×
253
                log.Error("Impossible payment state reached")
×
254
                return 0, fmt.Errorf("%w: payment is corrupted",
×
255
                        errPaymentStatusUnknown)
×
256
        }
257
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc