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

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

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

73.81
/channeldb/payment_status.go
1
package channeldb
2

3
import "fmt"
4

5
// PaymentStatus represent current status of payment.
6
type PaymentStatus byte
7

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

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

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

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

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

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

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

38
        case StatusInFlight:
3✔
39
                return "In Flight"
3✔
40

41
        case StatusSucceeded:
3✔
42
                return "Succeeded"
3✔
43

44
        case StatusFailed:
3✔
45
                return "Failed"
3✔
46

UNCOV
47
        default:
×
UNCOV
48
                return "Unknown"
×
49
        }
50
}
51

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

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

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

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

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

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

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

96
        // The payment has been attempted and is succeeded and is allowed to be
97
        // removed.
98
        case StatusSucceeded:
3✔
99
                return nil
3✔
100

101
        // Failed payments are allowed to be removed.
UNCOV
102
        case StatusFailed:
×
UNCOV
103
                return nil
×
104

UNCOV
105
        default:
×
UNCOV
106
                return fmt.Errorf("%w: %v", ErrUnknownPaymentStatus, ps)
×
107
        }
108
}
109

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

118
        // Inflight payments can be updated.
119
        case StatusInFlight:
3✔
120
                return nil
3✔
121

122
        // If the payment has a terminal condition, we won't allow any updates.
123
        case StatusSucceeded:
3✔
124
                return ErrPaymentAlreadySucceeded
3✔
125

126
        case StatusFailed:
3✔
127
                return ErrPaymentAlreadyFailed
3✔
128

UNCOV
129
        default:
×
UNCOV
130
                return fmt.Errorf("%w: %v", ErrUnknownPaymentStatus, ps)
×
131
        }
132
}
133

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

3✔
171
        var (
3✔
172
                inflight      bool
3✔
173
                htlcSettled   bool
3✔
174
                htlcFailed    bool
3✔
175
                paymentFailed bool
3✔
176
        )
3✔
177

3✔
178
        // If we have a failure reason, the payment is failed.
3✔
179
        if reason != nil {
6✔
180
                paymentFailed = true
3✔
181
        }
3✔
182

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

191
                if h.Settle != nil {
6✔
192
                        htlcSettled = true
3✔
193
                        continue
3✔
194
                }
195

196
                // If any of the HTLCs are not failed nor settled, we
197
                // still have inflight HTLCs.
198
                inflight = true
3✔
199
        }
200

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

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

220
        // If we have no in-flight HTLCs, and the payment failure is set, the
221
        // payment is considered failed.
222
        //
223
        // NOTE: when reaching this case, settled must be false.
224
        case paymentFailed:
3✔
225
                return StatusFailed, nil
3✔
226

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

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

244
        // Otherwise an impossible state is reached.
245
        //
246
        // NOTE: we should never end up here.
247
        default:
×
248
                log.Error("Impossible payment state reached")
×
249
                return 0, fmt.Errorf("%w: payment is corrupted",
×
250
                        errPaymentStatusUnknown)
×
251
        }
252
}
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