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

lightningnetwork / lnd / 17917482292

22 Sep 2025 01:50PM UTC coverage: 56.562% (-10.1%) from 66.668%
17917482292

Pull #10182

github

web-flow
Merge 9efe3bd8c into 055fb436e
Pull Request #10182: Aux feature bits

32 of 68 new or added lines in 5 files covered. (47.06%)

29734 existing lines in 467 files now uncovered.

98449 of 174056 relevant lines covered (56.56%)

1.18 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 {
2✔
36
        switch ps {
2✔
37
        case StatusInitiated:
2✔
38
                return "Initiated"
2✔
39

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

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

46
        case StatusFailed:
2✔
47
                return "Failed"
2✔
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 {
2✔
58
        switch ps {
2✔
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:
2✔
68
                return ErrPaymentInFlight
2✔
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:
2✔
77
                return nil
2✔
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 {
2✔
89
        switch ps {
2✔
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:
2✔
102
                return nil
2✔
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 {
2✔
117
        switch ps {
2✔
118
        // Newly created payments can be updated.
119
        case StatusInitiated:
2✔
120
                return nil
2✔
121

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

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

130
        case StatusFailed:
2✔
131
                return ErrPaymentAlreadyFailed
2✔
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) {
2✔
175

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

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

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

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

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