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

lightningnetwork / lnd / 19155841408

07 Nov 2025 02:03AM UTC coverage: 66.675% (-0.04%) from 66.712%
19155841408

Pull #10352

github

web-flow
Merge e4313eba8 into 096ab65b1
Pull Request #10352: [WIP] chainrpc: return Unavailable while notifier starts

137328 of 205965 relevant lines covered (66.68%)

21333.36 hits per line

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

95.4
/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 {
57✔
36
        switch ps {
57✔
37
        case StatusInitiated:
10✔
38
                return "Initiated"
10✔
39

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

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

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

49
        default:
14✔
50
                return "Unknown"
14✔
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 {
13✔
58
        switch ps {
13✔
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.
62
        case StatusInitiated:
2✔
63
                return ErrPaymentExists
2✔
64

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

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

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

79
        default:
1✔
80
                return fmt.Errorf("%w: %v", ErrUnknownPaymentStatus,
1✔
81
                        ps)
1✔
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 {
36✔
89
        switch ps {
36✔
90
        // The payment has been created but has no HTLCs and can be removed.
91
        case StatusInitiated:
1✔
92
                return nil
1✔
93

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

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

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

108
        default:
1✔
109
                return fmt.Errorf("%w: %v", ErrUnknownPaymentStatus,
1✔
110
                        ps)
1✔
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 {
234✔
117
        switch ps {
234✔
118
        // Newly created payments can be updated.
119
        case StatusInitiated:
63✔
120
                return nil
63✔
121

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

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

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

133
        default:
4✔
134
                return fmt.Errorf("%w: %v", ErrUnknownPaymentStatus,
4✔
135
                        ps)
4✔
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) {
736✔
175

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

736✔
183
        // If we have a failure reason, the payment is failed.
736✔
184
        if reason != nil {
825✔
185
                paymentFailed = true
89✔
186
        }
89✔
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 {
1,529✔
191
                if h.Failure != nil {
1,144✔
192
                        htlcFailed = true
351✔
193
                        continue
351✔
194
                }
195

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

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

206
        // Use the DB state to determine the status of the payment.
207
        switch {
736✔
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:
260✔
212
                return StatusInFlight, nil
260✔
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:
87✔
223
                return StatusSucceeded, nil
87✔
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:
62✔
230
                return StatusFailed, nil
62✔
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:
66✔
239
                return StatusInFlight, nil
66✔
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:
273✔
247
                return StatusInitiated, nil
273✔
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