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

lightningnetwork / lnd / 15574102646

11 Jun 2025 01:44AM UTC coverage: 68.554% (+9.9%) from 58.637%
15574102646

Pull #9652

github

web-flow
Merge eb863e46a into 92a5d35cf
Pull Request #9652: lnwallet/chancloser: fix flake in TestRbfCloseClosingNegotiationLocal

11 of 12 new or added lines in 1 file covered. (91.67%)

7276 existing lines in 84 files now uncovered.

134508 of 196208 relevant lines covered (68.55%)

44569.29 hits per line

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

92.07
/routing/payment_lifecycle.go
1
package routing
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "time"
8

9
        "github.com/btcsuite/btcd/btcec/v2"
10
        "github.com/davecgh/go-spew/spew"
11
        sphinx "github.com/lightningnetwork/lightning-onion"
12
        "github.com/lightningnetwork/lnd/channeldb"
13
        "github.com/lightningnetwork/lnd/fn/v2"
14
        "github.com/lightningnetwork/lnd/graph/db/models"
15
        "github.com/lightningnetwork/lnd/htlcswitch"
16
        "github.com/lightningnetwork/lnd/lntypes"
17
        "github.com/lightningnetwork/lnd/lnutils"
18
        "github.com/lightningnetwork/lnd/lnwire"
19
        "github.com/lightningnetwork/lnd/routing/route"
20
        "github.com/lightningnetwork/lnd/routing/shards"
21
        "github.com/lightningnetwork/lnd/tlv"
22
)
23

24
// ErrPaymentLifecycleExiting is used when waiting for htlc attempt result, but
25
// the payment lifecycle is exiting .
26
var ErrPaymentLifecycleExiting = errors.New("payment lifecycle exiting")
27

28
// switchResult is the result sent back from the switch after processing the
29
// HTLC.
30
type switchResult struct {
31
        // attempt is the HTLC sent to the switch.
32
        attempt *channeldb.HTLCAttempt
33

34
        // result is sent from the switch which contains either a preimage if
35
        // ths HTLC is settled or an error if it's failed.
36
        result *htlcswitch.PaymentResult
37
}
38

39
// paymentLifecycle holds all information about the current state of a payment
40
// needed to resume if from any point.
41
type paymentLifecycle struct {
42
        router                *ChannelRouter
43
        feeLimit              lnwire.MilliSatoshi
44
        identifier            lntypes.Hash
45
        paySession            PaymentSession
46
        shardTracker          shards.ShardTracker
47
        currentHeight         int32
48
        firstHopCustomRecords lnwire.CustomRecords
49

50
        // quit is closed to signal the sub goroutines of the payment lifecycle
51
        // to stop.
52
        quit chan struct{}
53

54
        // resultCollected is used to send the result returned from the switch
55
        // for a given HTLC attempt.
56
        resultCollected chan *switchResult
57

58
        // resultCollector is a function that is used to collect the result of
59
        // an HTLC attempt, which is always mounted to `p.collectResultAsync`
60
        // except in unit test, where we use a much simpler resultCollector to
61
        // decouple the test flow for the payment lifecycle.
62
        resultCollector func(attempt *channeldb.HTLCAttempt)
63
}
64

65
// newPaymentLifecycle initiates a new payment lifecycle and returns it.
66
func newPaymentLifecycle(r *ChannelRouter, feeLimit lnwire.MilliSatoshi,
67
        identifier lntypes.Hash, paySession PaymentSession,
68
        shardTracker shards.ShardTracker, currentHeight int32,
69
        firstHopCustomRecords lnwire.CustomRecords) *paymentLifecycle {
108✔
70

108✔
71
        p := &paymentLifecycle{
108✔
72
                router:                r,
108✔
73
                feeLimit:              feeLimit,
108✔
74
                identifier:            identifier,
108✔
75
                paySession:            paySession,
108✔
76
                shardTracker:          shardTracker,
108✔
77
                currentHeight:         currentHeight,
108✔
78
                quit:                  make(chan struct{}),
108✔
79
                resultCollected:       make(chan *switchResult, 1),
108✔
80
                firstHopCustomRecords: firstHopCustomRecords,
108✔
81
        }
108✔
82

108✔
83
        // Mount the result collector.
108✔
84
        p.resultCollector = p.collectResultAsync
108✔
85

108✔
86
        return p
108✔
87
}
108✔
88

89
// calcFeeBudget returns the available fee to be used for sending HTLC
90
// attempts.
91
func (p *paymentLifecycle) calcFeeBudget(
92
        feesPaid lnwire.MilliSatoshi) lnwire.MilliSatoshi {
212✔
93

212✔
94
        budget := p.feeLimit
212✔
95

212✔
96
        // We'll subtract the used fee from our fee budget. In case of
212✔
97
        // overflow, we need to check whether feesPaid exceeds our budget
212✔
98
        // already.
212✔
99
        if feesPaid <= budget {
424✔
100
                budget -= feesPaid
212✔
101
        } else {
218✔
102
                budget = 0
6✔
103
        }
6✔
104

105
        return budget
212✔
106
}
107

108
// stateStep defines an action to be taken in our payment lifecycle. We either
109
// quit, continue, or exit the lifecycle, see details below.
110
type stateStep uint8
111

112
const (
113
        // stepSkip is used when we need to skip the current lifecycle and jump
114
        // to the next one.
115
        stepSkip stateStep = iota
116

117
        // stepProceed is used when we can proceed the current lifecycle.
118
        stepProceed
119

120
        // stepExit is used when we need to quit the current lifecycle.
121
        stepExit
122
)
123

124
// decideNextStep is used to determine the next step in the payment lifecycle.
125
// It first checks whether the current state of the payment allows more HTLC
126
// attempts to be made. If allowed, it will return so the lifecycle can continue
127
// making new attempts. Otherwise, it checks whether we need to wait for the
128
// results of already sent attempts. If needed, it will block until one of the
129
// results is sent back. then process its result here. When there's no need to
130
// wait for results, the method will exit with `stepExit` such that the payment
131
// lifecycle loop will terminate.
132
func (p *paymentLifecycle) decideNextStep(
133
        payment DBMPPayment) (stateStep, error) {
158✔
134

158✔
135
        // Check whether we could make new HTLC attempts.
158✔
136
        allow, err := payment.AllowMoreAttempts()
158✔
137
        if err != nil {
162✔
138
                return stepExit, err
4✔
139
        }
4✔
140

141
        // Exit early we need to make more attempts.
142
        if allow {
222✔
143
                return stepProceed, nil
68✔
144
        }
68✔
145

146
        // We cannot make more attempts, we now check whether we need to wait
147
        // for results.
148
        wait, err := payment.NeedWaitAttempts()
92✔
149
        if err != nil {
94✔
150
                return stepExit, err
2✔
151
        }
2✔
152

153
        // If we are not allowed to make new HTLC attempts and there's no need
154
        // to wait, the lifecycle is done and we can exit.
155
        if !wait {
130✔
156
                return stepExit, nil
40✔
157
        }
40✔
158

159
        log.Tracef("Waiting for attempt results for payment %v", p.identifier)
56✔
160

56✔
161
        // Otherwise we wait for the result for one HTLC attempt then continue
56✔
162
        // the lifecycle.
56✔
163
        select {
56✔
164
        case r := <-p.resultCollected:
52✔
165
                log.Tracef("Received attempt result for payment %v",
52✔
166
                        p.identifier)
52✔
167

52✔
168
                // Handle the result here. If there's no error, we will return
52✔
169
                // stepSkip and move to the next lifecycle iteration, which will
52✔
170
                // refresh the payment and wait for the next attempt result, if
52✔
171
                // any.
52✔
172
                _, err := p.handleAttemptResult(r.attempt, r.result)
52✔
173

52✔
174
                // We would only get a DB-related error here, which will cause
52✔
175
                // us to abort the payment flow.
52✔
176
                if err != nil {
54✔
177
                        return stepExit, err
2✔
178
                }
2✔
179

180
        case <-p.quit:
2✔
181
                return stepExit, ErrPaymentLifecycleExiting
2✔
182

183
        case <-p.router.quit:
8✔
184
                return stepExit, ErrRouterShuttingDown
8✔
185
        }
186

187
        return stepSkip, nil
50✔
188
}
189

190
// resumePayment resumes the paymentLifecycle from the current state.
191
func (p *paymentLifecycle) resumePayment(ctx context.Context) ([32]byte,
192
        *route.Route, error) {
50✔
193

50✔
194
        // When the payment lifecycle loop exits, we make sure to signal any
50✔
195
        // sub goroutine of the HTLC attempt to exit, then wait for them to
50✔
196
        // return.
50✔
197
        defer p.stop()
50✔
198

50✔
199
        // If we had any existing attempts outstanding, we'll start by spinning
50✔
200
        // up goroutines that'll collect their results and deliver them to the
50✔
201
        // lifecycle loop below.
50✔
202
        payment, err := p.reloadInflightAttempts()
50✔
203
        if err != nil {
52✔
204
                return [32]byte{}, nil, err
2✔
205
        }
2✔
206

207
        // Get the payment status.
208
        status := payment.GetStatus()
48✔
209

48✔
210
        // exitWithErr is a helper closure that logs and returns an error.
48✔
211
        exitWithErr := func(err error) ([32]byte, *route.Route, error) {
64✔
212
                // Log an error with the latest payment status.
16✔
213
                //
16✔
214
                // NOTE: this `status` variable is reassigned in the loop
16✔
215
                // below. We could also call `payment.GetStatus` here, but in a
16✔
216
                // rare case when the critical log is triggered when using
16✔
217
                // postgres as db backend, the `payment` could be nil, causing
16✔
218
                // the payment fetching to return an error.
16✔
219
                log.Errorf("Payment %v with status=%v failed: %v", p.identifier,
16✔
220
                        status, err)
16✔
221

16✔
222
                return [32]byte{}, nil, err
16✔
223
        }
16✔
224

225
        // We'll continue until either our payment succeeds, or we encounter a
226
        // critical error during path finding.
227
lifecycle:
48✔
228
        for {
192✔
229
                // We update the payment state on every iteration.
144✔
230
                currentPayment, ps, err := p.reloadPayment()
144✔
231
                if err != nil {
144✔
232
                        return exitWithErr(err)
×
UNCOV
233
                }
×
234

235
                // Reassign status so it can be read in `exitWithErr`.
236
                status = currentPayment.GetStatus()
144✔
237

144✔
238
                // Reassign payment such that when the lifecycle exits, the
144✔
239
                // latest payment can be read when we access its terminal info.
144✔
240
                payment = currentPayment
144✔
241

144✔
242
                // We now proceed our lifecycle with the following tasks in
144✔
243
                // order,
144✔
244
                //   1. check context.
144✔
245
                //   2. request route.
144✔
246
                //   3. create HTLC attempt.
144✔
247
                //   4. send HTLC attempt.
144✔
248
                //   5. collect HTLC attempt result.
144✔
249
                //
144✔
250
                // Before we attempt any new shard, we'll check to see if we've
144✔
251
                // gone past the payment attempt timeout, or if the context was
144✔
252
                // cancelled, or the router is exiting. In any of these cases,
144✔
253
                // we'll stop this payment attempt short.
144✔
254
                if err := p.checkContext(ctx); err != nil {
146✔
255
                        return exitWithErr(err)
2✔
256
                }
2✔
257

258
                // Now decide the next step of the current lifecycle.
259
                step, err := p.decideNextStep(payment)
142✔
260
                if err != nil {
150✔
261
                        return exitWithErr(err)
8✔
262
                }
8✔
263

264
                switch step {
140✔
265
                // Exit the for loop and return below.
266
                case stepExit:
38✔
267
                        break lifecycle
38✔
268

269
                // Continue the for loop and skip the rest.
270
                case stepSkip:
48✔
271
                        continue lifecycle
48✔
272

273
                // Continue the for loop and proceed the rest.
274
                case stepProceed:
66✔
275

276
                // Unknown step received, exit with an error.
277
                default:
×
278
                        err = fmt.Errorf("unknown step: %v", step)
×
UNCOV
279
                        return exitWithErr(err)
×
280
                }
281

282
                // Now request a route to be used to create our HTLC attempt.
283
                rt, err := p.requestRoute(ps)
66✔
284
                if err != nil {
68✔
285
                        return exitWithErr(err)
2✔
286
                }
2✔
287

288
                // We may not be able to find a route for current attempt. In
289
                // that case, we continue the loop and move straight to the
290
                // next iteration in case there are results for inflight HTLCs
291
                // that still need to be collected.
292
                if rt == nil {
74✔
293
                        log.Errorf("No route found for payment %v",
10✔
294
                                p.identifier)
10✔
295

10✔
296
                        continue lifecycle
10✔
297
                }
298

299
                log.Tracef("Found route: %s", spew.Sdump(rt.Hops))
60✔
300

60✔
301
                // We found a route to try, create a new HTLC attempt to try.
60✔
302
                attempt, err := p.registerAttempt(rt, ps.RemainingAmt)
60✔
303
                if err != nil {
62✔
304
                        return exitWithErr(err)
2✔
305
                }
2✔
306

307
                // Once the attempt is created, send it to the htlcswitch.
308
                result, err := p.sendAttempt(attempt)
58✔
309
                if err != nil {
60✔
310
                        return exitWithErr(err)
2✔
311
                }
2✔
312

313
                // Now that the shard was successfully sent, launch a go
314
                // routine that will handle its result when its back.
315
                if result.err == nil {
110✔
316
                        p.resultCollector(attempt)
54✔
317
                }
54✔
318
        }
319

320
        // Once we are out the lifecycle loop, it means we've reached a
321
        // terminal condition. We either return the settled preimage or the
322
        // payment's failure reason.
323
        //
324
        // Optionally delete the failed attempts from the database.
325
        err = p.router.cfg.Control.DeleteFailedAttempts(p.identifier)
38✔
326
        if err != nil {
38✔
327
                log.Errorf("Error deleting failed htlc attempts for payment "+
×
328
                        "%v: %v", p.identifier, err)
×
UNCOV
329
        }
×
330

331
        htlc, failure := payment.TerminalInfo()
38✔
332
        if htlc != nil {
68✔
333
                return htlc.Settle.Preimage, &htlc.Route, nil
30✔
334
        }
30✔
335

336
        // Otherwise return the payment failure reason.
337
        return [32]byte{}, nil, *failure
14✔
338
}
339

340
// checkContext checks whether the payment context has been canceled.
341
// Cancellation occurs manually or if the context times out.
342
func (p *paymentLifecycle) checkContext(ctx context.Context) error {
150✔
343
        select {
150✔
344
        case <-ctx.Done():
14✔
345
                // If the context was canceled, we'll mark the payment as
14✔
346
                // failed. There are two cases to distinguish here: Either a
14✔
347
                // user-provided timeout was reached, or the context was
14✔
348
                // canceled, either to a manual cancellation or due to an
14✔
349
                // unknown error.
14✔
350
                var reason channeldb.FailureReason
14✔
351
                if errors.Is(ctx.Err(), context.DeadlineExceeded) {
20✔
352
                        reason = channeldb.FailureReasonTimeout
6✔
353
                        log.Warnf("Payment attempt not completed before "+
6✔
354
                                "context timeout, id=%s", p.identifier.String())
6✔
355
                } else {
14✔
356
                        reason = channeldb.FailureReasonCanceled
8✔
357
                        log.Warnf("Payment attempt context canceled, id=%s",
8✔
358
                                p.identifier.String())
8✔
359
                }
8✔
360

361
                // By marking the payment failed, depending on whether it has
362
                // inflight HTLCs or not, its status will now either be
363
                // `StatusInflight` or `StatusFailed`. In either case, no more
364
                // HTLCs will be attempted.
365
                err := p.router.cfg.Control.FailPayment(p.identifier, reason)
14✔
366
                if err != nil {
16✔
367
                        return fmt.Errorf("FailPayment got %w", err)
2✔
368
                }
2✔
369

370
        case <-p.router.quit:
4✔
371
                return fmt.Errorf("check payment timeout got: %w",
4✔
372
                        ErrRouterShuttingDown)
4✔
373

374
        // Fall through if we haven't hit our time limit.
375
        default:
138✔
376
        }
377

378
        return nil
144✔
379
}
380

381
// requestRoute is responsible for finding a route to be used to create an HTLC
382
// attempt.
383
func (p *paymentLifecycle) requestRoute(
384
        ps *channeldb.MPPaymentState) (*route.Route, error) {
74✔
385

74✔
386
        remainingFees := p.calcFeeBudget(ps.FeesPaid)
74✔
387

74✔
388
        // Query our payment session to construct a route.
74✔
389
        rt, err := p.paySession.RequestRoute(
74✔
390
                ps.RemainingAmt, remainingFees,
74✔
391
                uint32(ps.NumAttemptsInFlight), uint32(p.currentHeight),
74✔
392
                p.firstHopCustomRecords,
74✔
393
        )
74✔
394

74✔
395
        // Exit early if there's no error.
74✔
396
        if err == nil {
136✔
397
                // Allow the traffic shaper to add custom records to the
62✔
398
                // outgoing HTLC and also adjust the amount if needed.
62✔
399
                err = p.amendFirstHopData(rt)
62✔
400
                if err != nil {
62✔
401
                        return nil, err
×
UNCOV
402
                }
×
403

404
                return rt, nil
62✔
405
        }
406

407
        // Otherwise we need to handle the error.
408
        log.Warnf("Failed to find route for payment %v: %v", p.identifier, err)
18✔
409

18✔
410
        // If the error belongs to `noRouteError` set, it means a non-critical
18✔
411
        // error has happened during path finding, and we will mark the payment
18✔
412
        // failed with this reason. Otherwise, we'll return the critical error
18✔
413
        // found to abort the lifecycle.
18✔
414
        var routeErr noRouteError
18✔
415
        if !errors.As(err, &routeErr) {
22✔
416
                return nil, fmt.Errorf("requestRoute got: %w", err)
4✔
417
        }
4✔
418

419
        // It's the `paymentSession`'s responsibility to find a route for us
420
        // with the best effort. When it cannot find a path, we need to treat it
421
        // as a terminal condition and fail the payment no matter it has
422
        // inflight HTLCs or not.
423
        failureCode := routeErr.FailureReason()
14✔
424
        log.Warnf("Marking payment %v permanently failed with no route: %v",
14✔
425
                p.identifier, failureCode)
14✔
426

14✔
427
        err = p.router.cfg.Control.FailPayment(p.identifier, failureCode)
14✔
428
        if err != nil {
16✔
429
                return nil, fmt.Errorf("FailPayment got: %w", err)
2✔
430
        }
2✔
431

432
        // NOTE: we decide to not return the non-critical noRouteError here to
433
        // avoid terminating the payment lifecycle as there might be other
434
        // inflight HTLCs which we must wait for their results.
435
        return nil, nil
12✔
436
}
437

438
// stop signals any active shard goroutine to exit.
439
func (p *paymentLifecycle) stop() {
52✔
440
        close(p.quit)
52✔
441
}
52✔
442

443
// attemptResult holds the HTLC attempt and a possible error returned from
444
// sending it.
445
type attemptResult struct {
446
        // err is non-nil if a non-critical error was encountered when trying
447
        // to send the attempt, and we successfully updated the control tower
448
        // to reflect this error. This can be errors like not enough local
449
        // balance for the given route etc.
450
        err error
451

452
        // attempt is the attempt structure as recorded in the database.
453
        attempt *channeldb.HTLCAttempt
454
}
455

456
// collectResultAsync launches a goroutine that will wait for the result of the
457
// given HTLC attempt to be available then save its result in a map. Once
458
// received, it will send the result returned from the switch to channel
459
// `resultCollected`.
460
func (p *paymentLifecycle) collectResultAsync(attempt *channeldb.HTLCAttempt) {
52✔
461
        log.Debugf("Collecting result for attempt %v in payment %v",
52✔
462
                attempt.AttemptID, p.identifier)
52✔
463

52✔
464
        go func() {
104✔
465
                result, err := p.collectResult(attempt)
52✔
466
                if err != nil {
58✔
467
                        log.Errorf("Error collecting result for attempt %v in "+
6✔
468
                                "payment %v: %v", attempt.AttemptID,
6✔
469
                                p.identifier, err)
6✔
470

6✔
471
                        return
6✔
472
                }
6✔
473

474
                log.Debugf("Result collected for attempt %v in payment %v",
52✔
475
                        attempt.AttemptID, p.identifier)
52✔
476

52✔
477
                // Create a switch result and send it to the resultCollected
52✔
478
                // chan, which gets processed when the lifecycle is waiting for
52✔
479
                // a result to be received in decideNextStep.
52✔
480
                r := &switchResult{
52✔
481
                        attempt: attempt,
52✔
482
                        result:  result,
52✔
483
                }
52✔
484

52✔
485
                // Signal that a result has been collected.
52✔
486
                select {
52✔
487
                // Send the result so decideNextStep can proceed.
488
                case p.resultCollected <- r:
52✔
489

490
                case <-p.quit:
×
491
                        log.Debugf("Lifecycle exiting while collecting "+
×
UNCOV
492
                                "result for payment %v", p.identifier)
×
493

UNCOV
494
                case <-p.router.quit:
×
495
                }
496
        }()
497
}
498

499
// collectResult waits for the result of the given HTLC attempt to be sent by
500
// the switch and returns it.
501
func (p *paymentLifecycle) collectResult(
502
        attempt *channeldb.HTLCAttempt) (*htlcswitch.PaymentResult, error) {
76✔
503

76✔
504
        log.Tracef("Collecting result for attempt %v",
76✔
505
                lnutils.SpewLogClosure(attempt))
76✔
506

76✔
507
        result := &htlcswitch.PaymentResult{}
76✔
508

76✔
509
        // Regenerate the circuit for this attempt.
76✔
510
        circuit, err := attempt.Circuit()
76✔
511

76✔
512
        // TODO(yy): We generate this circuit to create the error decryptor,
76✔
513
        // which is then used in htlcswitch as the deobfuscator to decode the
76✔
514
        // error from `UpdateFailHTLC`. However, suppose it's an
76✔
515
        // `UpdateFulfillHTLC` message yet for some reason the sphinx packet is
76✔
516
        // failed to be generated, we'd miss settling it. This means we should
76✔
517
        // give it a second chance to try the settlement path in case
76✔
518
        // `GetAttemptResult` gives us back the preimage. And move the circuit
76✔
519
        // creation into htlcswitch so it's only constructed when there's a
76✔
520
        // failure message we need to decode.
76✔
521
        if err != nil {
76✔
522
                log.Debugf("Unable to generate circuit for attempt %v: %v",
×
523
                        attempt.AttemptID, err)
×
UNCOV
524
                return nil, err
×
UNCOV
525
        }
×
526

527
        // Using the created circuit, initialize the error decrypter, so we can
528
        // parse+decode any failures incurred by this payment within the
529
        // switch.
530
        errorDecryptor := &htlcswitch.SphinxErrorDecrypter{
76✔
531
                OnionErrorDecrypter: sphinx.NewOnionErrorDecrypter(circuit),
76✔
532
        }
76✔
533

76✔
534
        // Now ask the switch to return the result of the payment when
76✔
535
        // available.
76✔
536
        //
76✔
537
        // TODO(yy): consider using htlcswitch to create the `errorDecryptor`
76✔
538
        // since the htlc is already in db. This will also make the interface
76✔
539
        // `PaymentAttemptDispatcher` deeper and easier to use. Moreover, we'd
76✔
540
        // only create the decryptor when received a failure, further saving us
76✔
541
        // a few CPU cycles.
76✔
542
        resultChan, err := p.router.cfg.Payer.GetAttemptResult(
76✔
543
                attempt.AttemptID, p.identifier, errorDecryptor,
76✔
544
        )
76✔
545
        // Handle the switch error.
76✔
546
        if err != nil {
78✔
547
                log.Errorf("Failed getting result for attemptID %d "+
2✔
548
                        "from switch: %v", attempt.AttemptID, err)
2✔
549

2✔
550
                result.Error = err
2✔
551

2✔
552
                return result, nil
2✔
553
        }
2✔
554

555
        // The switch knows about this payment, we'll wait for a result to be
556
        // available.
557
        select {
74✔
558
        case r, ok := <-resultChan:
70✔
559
                if !ok {
78✔
560
                        return nil, htlcswitch.ErrSwitchExiting
8✔
561
                }
8✔
562

563
                result = r
68✔
564

565
        case <-p.quit:
2✔
566
                return nil, ErrPaymentLifecycleExiting
2✔
567

568
        case <-p.router.quit:
2✔
569
                return nil, ErrRouterShuttingDown
2✔
570
        }
571

572
        return result, nil
68✔
573
}
574

575
// registerAttempt is responsible for creating and saving an HTLC attempt in db
576
// by using the route info provided. The `remainingAmt` is used to decide
577
// whether this is the last attempt.
578
func (p *paymentLifecycle) registerAttempt(rt *route.Route,
579
        remainingAmt lnwire.MilliSatoshi) (*channeldb.HTLCAttempt, error) {
78✔
580

78✔
581
        // If this route will consume the last remaining amount to send
78✔
582
        // to the receiver, this will be our last shard (for now).
78✔
583
        isLastAttempt := rt.ReceiverAmt() == remainingAmt
78✔
584

78✔
585
        // Using the route received from the payment session, create a new
78✔
586
        // shard to send.
78✔
587
        attempt, err := p.createNewPaymentAttempt(rt, isLastAttempt)
78✔
588
        if err != nil {
82✔
589
                return nil, err
4✔
590
        }
4✔
591

592
        // Before sending this HTLC to the switch, we checkpoint the fresh
593
        // paymentID and route to the DB. This lets us know on startup the ID
594
        // of the payment that we attempted to send, such that we can query the
595
        // Switch for its whereabouts. The route is needed to handle the result
596
        // when it eventually comes back.
597
        err = p.router.cfg.Control.RegisterAttempt(
74✔
598
                p.identifier, &attempt.HTLCAttemptInfo,
74✔
599
        )
74✔
600

74✔
601
        return attempt, err
74✔
602
}
603

604
// createNewPaymentAttempt creates a new payment attempt from the given route.
605
func (p *paymentLifecycle) createNewPaymentAttempt(rt *route.Route,
606
        lastShard bool) (*channeldb.HTLCAttempt, error) {
78✔
607

78✔
608
        // Generate a new key to be used for this attempt.
78✔
609
        sessionKey, err := generateNewSessionKey()
78✔
610
        if err != nil {
78✔
UNCOV
611
                return nil, err
×
UNCOV
612
        }
×
613

614
        // We generate a new, unique payment ID that we will use for
615
        // this HTLC.
616
        attemptID, err := p.router.cfg.NextPaymentID()
78✔
617
        if err != nil {
78✔
UNCOV
618
                return nil, err
×
UNCOV
619
        }
×
620

621
        // Request a new shard from the ShardTracker. If this is an AMP
622
        // payment, and this is the last shard, the outstanding shards together
623
        // with this one will be enough for the receiver to derive all HTLC
624
        // preimages. If this a non-AMP payment, the ShardTracker will return a
625
        // simple shard with the payment's static payment hash.
626
        shard, err := p.shardTracker.NewShard(attemptID, lastShard)
78✔
627
        if err != nil {
80✔
628
                return nil, err
2✔
629
        }
2✔
630

631
        // If this shard carries MPP or AMP options, add them to the last hop
632
        // on the route.
633
        hop := rt.Hops[len(rt.Hops)-1]
76✔
634
        if shard.MPP() != nil {
90✔
635
                hop.MPP = shard.MPP()
14✔
636
        }
14✔
637

638
        if shard.AMP() != nil {
82✔
639
                hop.AMP = shard.AMP()
6✔
640
        }
6✔
641

642
        hash := shard.Hash()
76✔
643

76✔
644
        // We now have all the information needed to populate the current
76✔
645
        // attempt information.
76✔
646
        return channeldb.NewHtlcAttempt(
76✔
647
                attemptID, sessionKey, *rt, p.router.cfg.Clock.Now(), &hash,
76✔
648
        )
76✔
649
}
650

651
// sendAttempt attempts to send the current attempt to the switch to complete
652
// the payment. If this attempt fails, then we'll continue on to the next
653
// available route.
654
func (p *paymentLifecycle) sendAttempt(
655
        attempt *channeldb.HTLCAttempt) (*attemptResult, error) {
74✔
656

74✔
657
        log.Debugf("Sending HTLC attempt(id=%v, total_amt=%v, first_hop_amt=%d"+
74✔
658
                ") for payment %v", attempt.AttemptID,
74✔
659
                attempt.Route.TotalAmount, attempt.Route.FirstHopAmount.Val,
74✔
660
                p.identifier)
74✔
661

74✔
662
        rt := attempt.Route
74✔
663

74✔
664
        // Construct the first hop.
74✔
665
        firstHop := lnwire.NewShortChanIDFromInt(rt.Hops[0].ChannelID)
74✔
666

74✔
667
        // Craft an HTLC packet to send to the htlcswitch. The metadata within
74✔
668
        // this packet will be used to route the payment through the network,
74✔
669
        // starting with the first-hop.
74✔
670
        htlcAdd := &lnwire.UpdateAddHTLC{
74✔
671
                Amount:        rt.FirstHopAmount.Val.Int(),
74✔
672
                Expiry:        rt.TotalTimeLock,
74✔
673
                PaymentHash:   *attempt.Hash,
74✔
674
                CustomRecords: rt.FirstHopWireCustomRecords,
74✔
675
        }
74✔
676

74✔
677
        // Generate the raw encoded sphinx packet to be included along
74✔
678
        // with the htlcAdd message that we send directly to the
74✔
679
        // switch.
74✔
680
        onionBlob, err := attempt.OnionBlob()
74✔
681
        if err != nil {
74✔
682
                log.Errorf("Failed to create onion blob: attempt=%d in "+
×
683
                        "payment=%v, err:%v", attempt.AttemptID,
×
684
                        p.identifier, err)
×
685

×
UNCOV
686
                return p.failAttempt(attempt.AttemptID, err)
×
UNCOV
687
        }
×
688

689
        htlcAdd.OnionBlob = onionBlob
74✔
690

74✔
691
        // Send it to the Switch. When this method returns we assume
74✔
692
        // the Switch successfully has persisted the payment attempt,
74✔
693
        // such that we can resume waiting for the result after a
74✔
694
        // restart.
74✔
695
        err = p.router.cfg.Payer.SendHTLC(firstHop, attempt.AttemptID, htlcAdd)
74✔
696
        if err != nil {
90✔
697
                log.Errorf("Failed sending attempt %d for payment %v to "+
16✔
698
                        "switch: %v", attempt.AttemptID, p.identifier, err)
16✔
699

16✔
700
                return p.handleSwitchErr(attempt, err)
16✔
701
        }
16✔
702

703
        log.Debugf("Attempt %v for payment %v successfully sent to switch, "+
64✔
704
                "route: %v", attempt.AttemptID, p.identifier, &attempt.Route)
64✔
705

64✔
706
        return &attemptResult{
64✔
707
                attempt: attempt,
64✔
708
        }, nil
64✔
709
}
710

711
// amendFirstHopData is a function that calls the traffic shaper to allow it to
712
// add custom records to the outgoing HTLC and also adjust the amount if
713
// needed.
714
func (p *paymentLifecycle) amendFirstHopData(rt *route.Route) error {
80✔
715
        // The first hop amount on the route is the full route amount if not
80✔
716
        // overwritten by the traffic shaper. So we set the initial value now
80✔
717
        // and potentially overwrite it later.
80✔
718
        rt.FirstHopAmount = tlv.NewRecordT[tlv.TlvType0](
80✔
719
                tlv.NewBigSizeT(rt.TotalAmount),
80✔
720
        )
80✔
721

80✔
722
        // By default, we set the first hop custom records to the initial
80✔
723
        // value requested by the RPC. The traffic shaper may overwrite this
80✔
724
        // value.
80✔
725
        rt.FirstHopWireCustomRecords = p.firstHopCustomRecords
80✔
726

80✔
727
        // extraDataRequest is a helper struct to pass the custom records and
80✔
728
        // amount back from the traffic shaper.
80✔
729
        type extraDataRequest struct {
80✔
730
                customRecords fn.Option[lnwire.CustomRecords]
80✔
731

80✔
732
                amount fn.Option[lnwire.MilliSatoshi]
80✔
733
        }
80✔
734

80✔
735
        // If a hook exists that may affect our outgoing message, we call it now
80✔
736
        // and apply its side effects to the UpdateAddHTLC message.
80✔
737
        result, err := fn.MapOptionZ(
80✔
738
                p.router.cfg.TrafficShaper,
80✔
739
                //nolint:ll
80✔
740
                func(ts htlcswitch.AuxTrafficShaper) fn.Result[extraDataRequest] {
154✔
741
                        newAmt, newRecords, err := ts.ProduceHtlcExtraData(
74✔
742
                                rt.TotalAmount, p.firstHopCustomRecords,
74✔
743
                        )
74✔
744
                        if err != nil {
74✔
UNCOV
745
                                return fn.Err[extraDataRequest](err)
×
UNCOV
746
                        }
×
747

748
                        // Make sure we only received valid records.
749
                        if err := newRecords.Validate(); err != nil {
74✔
UNCOV
750
                                return fn.Err[extraDataRequest](err)
×
751
                        }
×
752

753
                        log.Debugf("Aux traffic shaper returned custom "+
74✔
754
                                "records %v and amount %d msat for HTLC",
74✔
755
                                spew.Sdump(newRecords), newAmt)
74✔
756

74✔
757
                        return fn.Ok(extraDataRequest{
74✔
758
                                customRecords: fn.Some(newRecords),
74✔
759
                                amount:        fn.Some(newAmt),
74✔
760
                        })
74✔
761
                },
762
        ).Unpack()
763
        if err != nil {
80✔
764
                return fmt.Errorf("traffic shaper failed to produce extra "+
×
UNCOV
765
                        "data: %w", err)
×
UNCOV
766
        }
×
767

768
        // Apply the side effects to the UpdateAddHTLC message.
769
        result.customRecords.WhenSome(func(records lnwire.CustomRecords) {
154✔
770
                rt.FirstHopWireCustomRecords = records
74✔
771
        })
74✔
772
        result.amount.WhenSome(func(amount lnwire.MilliSatoshi) {
154✔
773
                rt.FirstHopAmount = tlv.NewRecordT[tlv.TlvType0](
74✔
774
                        tlv.NewBigSizeT(amount),
74✔
775
                )
74✔
776
        })
74✔
777

778
        return nil
80✔
779
}
780

781
// failAttemptAndPayment fails both the payment and its attempt via the
782
// router's control tower, which marks the payment as failed in db.
783
func (p *paymentLifecycle) failPaymentAndAttempt(
784
        attemptID uint64, reason *channeldb.FailureReason,
785
        sendErr error) (*attemptResult, error) {
16✔
786

16✔
787
        log.Errorf("Payment %v failed: final_outcome=%v, raw_err=%v",
16✔
788
                p.identifier, *reason, sendErr)
16✔
789

16✔
790
        // Fail the payment via control tower.
16✔
791
        //
16✔
792
        // NOTE: we must fail the payment first before failing the attempt.
16✔
793
        // Otherwise, once the attempt is marked as failed, another goroutine
16✔
794
        // might make another attempt while we are failing the payment.
16✔
795
        err := p.router.cfg.Control.FailPayment(p.identifier, *reason)
16✔
796
        if err != nil {
16✔
797
                log.Errorf("Unable to fail payment: %v", err)
×
UNCOV
798
                return nil, err
×
UNCOV
799
        }
×
800

801
        // Fail the attempt.
802
        return p.failAttempt(attemptID, sendErr)
16✔
803
}
804

805
// handleSwitchErr inspects the given error from the Switch and determines
806
// whether we should make another payment attempt, or if it should be
807
// considered a terminal error. Terminal errors will be recorded with the
808
// control tower. It analyzes the sendErr for the payment attempt received from
809
// the switch and updates mission control and/or channel policies. Depending on
810
// the error type, the error is either the final outcome of the payment or we
811
// need to continue with an alternative route. A final outcome is indicated by
812
// a non-nil reason value.
813
func (p *paymentLifecycle) handleSwitchErr(attempt *channeldb.HTLCAttempt,
814
        sendErr error) (*attemptResult, error) {
52✔
815

52✔
816
        internalErrorReason := channeldb.FailureReasonError
52✔
817
        attemptID := attempt.AttemptID
52✔
818

52✔
819
        // reportAndFail is a helper closure that reports the failure to the
52✔
820
        // mission control, which helps us to decide whether we want to retry
52✔
821
        // the payment or not. If a non nil reason is returned from mission
52✔
822
        // control, it will further fail the payment via control tower.
52✔
823
        reportAndFail := func(srcIdx *int,
52✔
824
                msg lnwire.FailureMessage) (*attemptResult, error) {
96✔
825

44✔
826
                // Report outcome to mission control.
44✔
827
                reason, err := p.router.cfg.MissionControl.ReportPaymentFail(
44✔
828
                        attemptID, &attempt.Route, srcIdx, msg,
44✔
829
                )
44✔
830
                if err != nil {
44✔
831
                        log.Errorf("Error reporting payment result to mc: %v",
×
832
                                err)
×
833

×
UNCOV
834
                        reason = &internalErrorReason
×
UNCOV
835
                }
×
836

837
                // Fail the attempt only if there's no reason.
838
                if reason == nil {
84✔
839
                        // Fail the attempt.
40✔
840
                        return p.failAttempt(attemptID, sendErr)
40✔
841
                }
40✔
842

843
                // Otherwise fail both the payment and the attempt.
844
                return p.failPaymentAndAttempt(attemptID, reason, sendErr)
10✔
845
        }
846

847
        // If this attempt ID is unknown to the Switch, it means it was never
848
        // checkpointed and forwarded by the switch before a restart. In this
849
        // case we can safely send a new payment attempt, and wait for its
850
        // result to be available.
851
        if errors.Is(sendErr, htlcswitch.ErrPaymentIDNotFound) {
54✔
852
                log.Warnf("Failing attempt=%v for payment=%v as it's not "+
2✔
853
                        "found in the Switch", attempt.AttemptID, p.identifier)
2✔
854

2✔
855
                return p.failAttempt(attemptID, sendErr)
2✔
856
        }
2✔
857

858
        if errors.Is(sendErr, htlcswitch.ErrUnreadableFailureMessage) {
52✔
859
                log.Warn("Unreadable failure when sending htlc: id=%v, hash=%v",
2✔
860
                        attempt.AttemptID, attempt.Hash)
2✔
861

2✔
862
                // Since this error message cannot be decrypted, we will send a
2✔
863
                // nil error message to our mission controller and fail the
2✔
864
                // payment.
2✔
865
                return reportAndFail(nil, nil)
2✔
866
        }
2✔
867

868
        // If the error is a ClearTextError, we have received a valid wire
869
        // failure message, either from our own outgoing link or from a node
870
        // down the route. If the error is not related to the propagation of
871
        // our payment, we can stop trying because an internal error has
872
        // occurred.
873
        var rtErr htlcswitch.ClearTextError
48✔
874
        ok := errors.As(sendErr, &rtErr)
48✔
875
        if !ok {
54✔
876
                return p.failPaymentAndAttempt(
6✔
877
                        attemptID, &internalErrorReason, sendErr,
6✔
878
                )
6✔
879
        }
6✔
880

881
        // failureSourceIdx is the index of the node that the failure occurred
882
        // at. If the ClearTextError received is not a ForwardingError the
883
        // payment error occurred at our node, so we leave this value as 0
884
        // to indicate that the failure occurred locally. If the error is a
885
        // ForwardingError, it did not originate at our node, so we set
886
        // failureSourceIdx to the index of the node where the failure occurred.
887
        failureSourceIdx := 0
42✔
888
        var source *htlcswitch.ForwardingError
42✔
889
        ok = errors.As(rtErr, &source)
42✔
890
        if ok {
84✔
891
                failureSourceIdx = source.FailureSourceIdx
42✔
892
        }
42✔
893

894
        // Extract the wire failure and apply channel update if it contains one.
895
        // If we received an unknown failure message from a node along the
896
        // route, the failure message will be nil.
897
        failureMessage := rtErr.WireMessage()
42✔
898
        err := p.handleFailureMessage(
42✔
899
                &attempt.Route, failureSourceIdx, failureMessage,
42✔
900
        )
42✔
901
        if err != nil {
42✔
902
                return p.failPaymentAndAttempt(
×
903
                        attemptID, &internalErrorReason, sendErr,
×
UNCOV
904
                )
×
UNCOV
905
        }
×
906

907
        log.Tracef("Node=%v reported failure when sending htlc",
42✔
908
                failureSourceIdx)
42✔
909

42✔
910
        return reportAndFail(&failureSourceIdx, failureMessage)
42✔
911
}
912

913
// handleFailureMessage tries to apply a channel update present in the failure
914
// message if any.
915
func (p *paymentLifecycle) handleFailureMessage(rt *route.Route,
916
        errorSourceIdx int, failure lnwire.FailureMessage) error {
42✔
917

42✔
918
        if failure == nil {
44✔
919
                return nil
2✔
920
        }
2✔
921

922
        // It makes no sense to apply our own channel updates.
923
        if errorSourceIdx == 0 {
46✔
924
                log.Errorf("Channel update of ourselves received")
6✔
925

6✔
926
                return nil
6✔
927
        }
6✔
928

929
        // Extract channel update if the error contains one.
930
        update := p.router.extractChannelUpdate(failure)
40✔
931
        if update == nil {
64✔
932
                return nil
24✔
933
        }
24✔
934

935
        // Parse pubkey to allow validation of the channel update. This should
936
        // always succeed, otherwise there is something wrong in our
937
        // implementation. Therefore, return an error.
938
        errVertex := rt.Hops[errorSourceIdx-1].PubKeyBytes
22✔
939
        errSource, err := btcec.ParsePubKey(errVertex[:])
22✔
940
        if err != nil {
22✔
941
                log.Errorf("Cannot parse pubkey: idx=%v, pubkey=%v",
×
942
                        errorSourceIdx, errVertex)
×
943

×
UNCOV
944
                return err
×
UNCOV
945
        }
×
946

947
        var (
22✔
948
                isAdditionalEdge bool
22✔
949
                policy           *models.CachedEdgePolicy
22✔
950
        )
22✔
951

22✔
952
        // Before we apply the channel update, we need to decide whether the
22✔
953
        // update is for additional (ephemeral) edge or normal edge stored in
22✔
954
        // db.
22✔
955
        //
22✔
956
        // Note: the p.paySession might be nil here if it's called inside
22✔
957
        // SendToRoute where there's no payment lifecycle.
22✔
958
        if p.paySession != nil {
38✔
959
                policy = p.paySession.GetAdditionalEdgePolicy(
16✔
960
                        errSource, update.ShortChannelID.ToUint64(),
16✔
961
                )
16✔
962
                if policy != nil {
26✔
963
                        isAdditionalEdge = true
10✔
964
                }
10✔
965
        }
966

967
        // Apply channel update to additional edge policy.
968
        if isAdditionalEdge {
32✔
969
                if !p.paySession.UpdateAdditionalEdge(
10✔
970
                        update, errSource, policy) {
10✔
971

×
972
                        log.Debugf("Invalid channel update received: node=%v",
×
UNCOV
973
                                errVertex)
×
UNCOV
974
                }
×
975
                return nil
10✔
976
        }
977

978
        // Apply channel update to the channel edge policy in our db.
979
        if !p.router.cfg.ApplyChannelUpdate(update) {
28✔
980
                log.Debugf("Invalid channel update received: node=%v",
10✔
981
                        errVertex)
10✔
982
        }
10✔
983
        return nil
18✔
984
}
985

986
// failAttempt calls control tower to fail the current payment attempt.
987
func (p *paymentLifecycle) failAttempt(attemptID uint64,
988
        sendError error) (*attemptResult, error) {
52✔
989

52✔
990
        log.Warnf("Attempt %v for payment %v failed: %v", attemptID,
52✔
991
                p.identifier, sendError)
52✔
992

52✔
993
        failInfo := marshallError(
52✔
994
                sendError,
52✔
995
                p.router.cfg.Clock.Now(),
52✔
996
        )
52✔
997

52✔
998
        // Now that we are failing this payment attempt, cancel the shard with
52✔
999
        // the ShardTracker such that it can derive the correct hash for the
52✔
1000
        // next attempt.
52✔
1001
        if err := p.shardTracker.CancelShard(attemptID); err != nil {
52✔
UNCOV
1002
                return nil, err
×
UNCOV
1003
        }
×
1004

1005
        attempt, err := p.router.cfg.Control.FailAttempt(
52✔
1006
                p.identifier, attemptID, failInfo,
52✔
1007
        )
52✔
1008
        if err != nil {
60✔
1009
                return nil, err
8✔
1010
        }
8✔
1011

1012
        return &attemptResult{
44✔
1013
                attempt: attempt,
44✔
1014
                err:     sendError,
44✔
1015
        }, nil
44✔
1016
}
1017

1018
// marshallError marshall an error as received from the switch to a structure
1019
// that is suitable for database storage.
1020
func marshallError(sendError error, time time.Time) *channeldb.HTLCFailInfo {
52✔
1021
        response := &channeldb.HTLCFailInfo{
52✔
1022
                FailTime: time,
52✔
1023
        }
52✔
1024

52✔
1025
        switch {
52✔
1026
        case errors.Is(sendError, htlcswitch.ErrPaymentIDNotFound):
2✔
1027
                response.Reason = channeldb.HTLCFailInternal
2✔
1028
                return response
2✔
1029

1030
        case errors.Is(sendError, htlcswitch.ErrUnreadableFailureMessage):
2✔
1031
                response.Reason = channeldb.HTLCFailUnreadable
2✔
1032
                return response
2✔
1033
        }
1034

1035
        var rtErr htlcswitch.ClearTextError
48✔
1036
        ok := errors.As(sendError, &rtErr)
48✔
1037
        if !ok {
54✔
1038
                response.Reason = channeldb.HTLCFailInternal
6✔
1039
                return response
6✔
1040
        }
6✔
1041

1042
        message := rtErr.WireMessage()
42✔
1043
        if message != nil {
82✔
1044
                response.Reason = channeldb.HTLCFailMessage
40✔
1045
                response.Message = message
40✔
1046
        } else {
42✔
1047
                response.Reason = channeldb.HTLCFailUnknown
2✔
1048
        }
2✔
1049

1050
        // If the ClearTextError received is a ForwardingError, the error
1051
        // originated from a node along the route, not locally on our outgoing
1052
        // link. We set failureSourceIdx to the index of the node where the
1053
        // failure occurred. If the error is not a ForwardingError, the failure
1054
        // occurred at our node, so we leave the index as 0 to indicate that
1055
        // we failed locally.
1056
        var fErr *htlcswitch.ForwardingError
42✔
1057
        ok = errors.As(rtErr, &fErr)
42✔
1058
        if ok {
84✔
1059
                response.FailureSourceIndex = uint32(fErr.FailureSourceIdx)
42✔
1060
        }
42✔
1061

1062
        return response
42✔
1063
}
1064

1065
// patchLegacyPaymentHash will make a copy of the passed attempt and sets its
1066
// Hash field to be the payment hash if it's nil.
1067
//
1068
// NOTE: For legacy payments, which were created before the AMP feature was
1069
// enabled, the `Hash` field in their HTLC attempts is nil. In that case, we use
1070
// the payment hash as the `attempt.Hash` as they are identical.
1071
func (p *paymentLifecycle) patchLegacyPaymentHash(
1072
        a channeldb.HTLCAttempt) channeldb.HTLCAttempt {
8✔
1073

8✔
1074
        // Exit early if this is not a legacy attempt.
8✔
1075
        if a.Hash != nil {
14✔
1076
                return a
6✔
1077
        }
6✔
1078

1079
        // Log a warning if the user is still using legacy payments, which has
1080
        // weaker support.
1081
        log.Warnf("Found legacy htlc attempt %v in payment %v", a.AttemptID,
2✔
1082
                p.identifier)
2✔
1083

2✔
1084
        // Set the attempt's hash to be the payment hash, which is the payment's
2✔
1085
        // `PaymentHash`` in the `PaymentCreationInfo`. For legacy payments
2✔
1086
        // before AMP feature, the `Hash` field was not set so we use the
2✔
1087
        // payment hash instead.
2✔
1088
        //
2✔
1089
        // NOTE: During the router's startup, we have a similar logic in
2✔
1090
        // `resumePayments`, in which we will use the payment hash instead if
2✔
1091
        // the attempt's hash is nil.
2✔
1092
        a.Hash = &p.identifier
2✔
1093

2✔
1094
        return a
2✔
1095
}
1096

1097
// reloadInflightAttempts is called when the payment lifecycle is resumed after
1098
// a restart. It reloads all inflight attempts from the control tower and
1099
// collects the results of the attempts that have been sent before.
1100
func (p *paymentLifecycle) reloadInflightAttempts() (DBMPPayment, error) {
52✔
1101
        payment, err := p.router.cfg.Control.FetchPayment(p.identifier)
52✔
1102
        if err != nil {
54✔
1103
                return nil, err
2✔
1104
        }
2✔
1105

1106
        for _, a := range payment.InFlightHTLCs() {
58✔
1107
                a := a
8✔
1108

8✔
1109
                log.Infof("Resuming HTLC attempt %v for payment %v",
8✔
1110
                        a.AttemptID, p.identifier)
8✔
1111

8✔
1112
                // Potentially attach the payment hash to the `Hash` field if
8✔
1113
                // it's a legacy payment.
8✔
1114
                a = p.patchLegacyPaymentHash(a)
8✔
1115

8✔
1116
                p.resultCollector(&a)
8✔
1117
        }
8✔
1118

1119
        return payment, nil
50✔
1120
}
1121

1122
// reloadPayment returns the latest payment found in the db (control tower).
1123
func (p *paymentLifecycle) reloadPayment() (DBMPPayment,
1124
        *channeldb.MPPaymentState, error) {
144✔
1125

144✔
1126
        // Read the db to get the latest state of the payment.
144✔
1127
        payment, err := p.router.cfg.Control.FetchPayment(p.identifier)
144✔
1128
        if err != nil {
144✔
UNCOV
1129
                return nil, nil, err
×
UNCOV
1130
        }
×
1131

1132
        ps := payment.GetState()
144✔
1133
        remainingFees := p.calcFeeBudget(ps.FeesPaid)
144✔
1134

144✔
1135
        log.Debugf("Payment %v: status=%v, active_shards=%v, rem_value=%v, "+
144✔
1136
                "fee_limit=%v", p.identifier, payment.GetStatus(),
144✔
1137
                ps.NumAttemptsInFlight, ps.RemainingAmt, remainingFees)
144✔
1138

144✔
1139
        return payment, ps, nil
144✔
1140
}
1141

1142
// handleAttemptResult processes the result of an HTLC attempt returned from
1143
// the htlcswitch.
1144
func (p *paymentLifecycle) handleAttemptResult(attempt *channeldb.HTLCAttempt,
1145
        result *htlcswitch.PaymentResult) (*attemptResult, error) {
74✔
1146

74✔
1147
        // If the result has an error, we need to further process it by failing
74✔
1148
        // the attempt and maybe fail the payment.
74✔
1149
        if result.Error != nil {
116✔
1150
                return p.handleSwitchErr(attempt, result.Error)
42✔
1151
        }
42✔
1152

1153
        // We got an attempt settled result back from the switch.
1154
        log.Debugf("Payment(%v): attempt(%v) succeeded", p.identifier,
38✔
1155
                attempt.AttemptID)
38✔
1156

38✔
1157
        // Report success to mission control.
38✔
1158
        err := p.router.cfg.MissionControl.ReportPaymentSuccess(
38✔
1159
                attempt.AttemptID, &attempt.Route,
38✔
1160
        )
38✔
1161
        if err != nil {
38✔
UNCOV
1162
                log.Errorf("Error reporting payment success to mc: %v", err)
×
UNCOV
1163
        }
×
1164

1165
        // In case of success we atomically store settle result to the DB and
1166
        // move the shard to the settled state.
1167
        htlcAttempt, err := p.router.cfg.Control.SettleAttempt(
38✔
1168
                p.identifier, attempt.AttemptID,
38✔
1169
                &channeldb.HTLCSettleInfo{
38✔
1170
                        Preimage:   result.Preimage,
38✔
1171
                        SettleTime: p.router.cfg.Clock.Now(),
38✔
1172
                },
38✔
1173
        )
38✔
1174
        if err != nil {
42✔
1175
                log.Errorf("Error settling attempt %v for payment %v with "+
4✔
1176
                        "preimage %v: %v", attempt.AttemptID, p.identifier,
4✔
1177
                        result.Preimage, err)
4✔
1178

4✔
1179
                // We won't mark the attempt as failed since we already have
4✔
1180
                // the preimage.
4✔
1181
                return nil, err
4✔
1182
        }
4✔
1183

1184
        return &attemptResult{
34✔
1185
                attempt: htlcAttempt,
34✔
1186
        }, nil
34✔
1187
}
1188

1189
// collectAndHandleResult waits for the result for the given attempt to be
1190
// available from the Switch, then records the attempt outcome with the control
1191
// tower. An attemptResult is returned, indicating the final outcome of this
1192
// HTLC attempt.
1193
func (p *paymentLifecycle) collectAndHandleResult(
1194
        attempt *channeldb.HTLCAttempt) (*attemptResult, error) {
30✔
1195

30✔
1196
        result, err := p.collectResult(attempt)
30✔
1197
        if err != nil {
36✔
1198
                return nil, err
6✔
1199
        }
6✔
1200

1201
        return p.handleAttemptResult(attempt, result)
24✔
1202
}
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