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

lightningnetwork / lnd / 15100227105

18 May 2025 09:28PM UTC coverage: 68.997% (+0.007%) from 68.99%
15100227105

Pull #9822

github

web-flow
Merge 40492099f into 3707b1fb7
Pull Request #9822: Refactor Payments Code (1|?)

414 of 501 new or added lines in 16 files covered. (82.63%)

49 existing lines in 19 files now uncovered.

133955 of 194145 relevant lines covered (69.0%)

22105.67 hits per line

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

89.17
/payments/payment.go
1
package payments
2

3
import (
4
        "bytes"
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/lntypes"
13
        "github.com/lightningnetwork/lnd/lnutils"
14
        "github.com/lightningnetwork/lnd/lnwire"
15

16
        "github.com/lightningnetwork/lnd/routing/route"
17
)
18

19
// MPPayment is a wrapper around a payment's PaymentCreationInfo and
20
// HTLCAttempts. All payments will have the PaymentCreationInfo set, any
21
// HTLCs made in attempts to be completed will populated in the HTLCs slice.
22
// Each populated HTLCAttempt represents an attempted HTLC, each of which may
23
// have the associated Settle or Fail struct populated if the HTLC is no longer
24
// in-flight.
25
type MPPayment struct {
26
        // SequenceNum is a unique identifier used to sort the payments in
27
        // order of creation.
28
        SequenceNum uint64
29

30
        // Info holds all static information about this payment, and is
31
        // populated when the payment is initiated.
32
        Info *PaymentCreationInfo
33

34
        // HTLCs holds the information about individual HTLCs that we send in
35
        // order to make the payment.
36
        HTLCs []HTLCAttempt
37

38
        // FailureReason is the failure reason code indicating the reason the
39
        // payment failed.
40
        //
41
        // NOTE: Will only be set once the daemon has given up on the payment
42
        // altogether.
43
        FailureReason *FailureReason
44

45
        // Status is the current PaymentStatus of this payment.
46
        Status PaymentStatus
47

48
        // State is the current state of the payment that holds a number of key
49
        // insights and is used to determine what to do on each payment loop
50
        // iteration.
51
        State *MPPaymentState
52
}
53

54
// Terminated returns a bool to specify whether the payment is in a terminal
55
// state.
56
func (m *MPPayment) Terminated() bool {
56✔
57
        // If the payment is in terminal state, it cannot be updated.
56✔
58
        return m.Status.Updatable() != nil
56✔
59
}
56✔
60

61
// TerminalInfo returns any HTLC settle info recorded. If no settle info is
62
// recorded, any payment level failure will be returned. If neither a settle
63
// nor a failure is recorded, both return values will be nil.
64
func (m *MPPayment) TerminalInfo() (*HTLCAttempt, *FailureReason) {
741✔
65
        for _, h := range m.HTLCs {
1,493✔
66
                if h.Settle != nil {
862✔
67
                        return &h, nil
110✔
68
                }
110✔
69
        }
70

71
        return nil, m.FailureReason
634✔
72
}
73

74
// SentAmt returns the sum of sent amount and fees for HTLCs that are either
75
// settled or still in flight.
76
func (m *MPPayment) SentAmt() (lnwire.MilliSatoshi, lnwire.MilliSatoshi) {
783✔
77
        var sent, fees lnwire.MilliSatoshi
783✔
78
        for _, h := range m.HTLCs {
1,603✔
79
                if h.Failure != nil {
1,177✔
80
                        continue
357✔
81
                }
82

83
                // The attempt was not failed, meaning the amount was
84
                // potentially sent to the receiver.
85
                sent += h.Route.ReceiverAmt()
466✔
86
                fees += h.Route.TotalFees()
466✔
87
        }
88

89
        return sent, fees
783✔
90
}
91

92
// InFlightHTLCs returns the HTLCs that are still in-flight, meaning they have
93
// not been settled or failed.
94
func (m *MPPayment) InFlightHTLCs() []HTLCAttempt {
802✔
95
        var inflights []HTLCAttempt
802✔
96
        for _, h := range m.HTLCs {
1,629✔
97
                if h.Settle != nil || h.Failure != nil {
1,281✔
98
                        continue
454✔
99
                }
100

101
                inflights = append(inflights, h)
376✔
102
        }
103

104
        return inflights
802✔
105
}
106

107
// GetAttempt returns the specified htlc attempt on the payment.
108
func (m *MPPayment) GetAttempt(id uint64) (*HTLCAttempt, error) {
9✔
109
        // TODO(yy): iteration can be slow, make it into a tree or use BS.
9✔
110
        for _, htlc := range m.HTLCs {
18✔
111
                htlc := htlc
9✔
112
                if htlc.AttemptID == id {
18✔
113
                        return &htlc, nil
9✔
114
                }
9✔
115
        }
116

NEW
117
        return nil, errors.New("htlc attempt not found on payment")
×
118
}
119

120
// Registrable returns an error to specify whether adding more HTLCs to the
121
// payment with its current status is allowed. A payment can accept new HTLC
122
// registrations when it's newly created, or none of its HTLCs is in a terminal
123
// state.
124
func (m *MPPayment) Registrable() error {
130✔
125
        // If updating the payment is not allowed, we can't register new HTLCs.
130✔
126
        // Otherwise, the status must be either `StatusInitiated` or
130✔
127
        // `StatusInFlight`.
130✔
128
        if err := m.Status.Updatable(); err != nil {
149✔
129
                return err
19✔
130
        }
19✔
131

132
        // Exit early if this is not inflight.
133
        if m.Status != StatusInFlight {
163✔
134
                return nil
49✔
135
        }
49✔
136

137
        // There are still inflight HTLCs and we need to check whether there
138
        // are settled HTLCs or the payment is failed. If we already have
139
        // settled HTLCs, we won't allow adding more HTLCs.
140
        if m.State.HasSettledHTLC {
75✔
141
                return ErrPaymentPendingSettled
7✔
142
        }
7✔
143

144
        // If the payment is already failed, we won't allow adding more HTLCs.
145
        if m.State.PaymentFailed {
67✔
146
                return ErrPaymentPendingFailed
6✔
147
        }
6✔
148

149
        // Otherwise we can add more HTLCs.
150
        return nil
55✔
151
}
152

153
// setState creates and attaches a new MPPaymentState to the payment. It also
154
// updates the payment's status based on its current state.
155
func (m *MPPayment) setState() error {
721✔
156
        // Fetch the total amount and fees that has already been sent in
721✔
157
        // settled and still in-flight shards.
721✔
158
        sentAmt, fees := m.SentAmt()
721✔
159

721✔
160
        // Sanity check we haven't sent a value larger than the payment amount.
721✔
161
        totalAmt := m.Info.Value
721✔
162
        if sentAmt > totalAmt {
722✔
163
                return fmt.Errorf("%w: sent=%v, total=%v", ErrSentExceedsTotal,
1✔
164
                        sentAmt, totalAmt)
1✔
165
        }
1✔
166

167
        // Get any terminal info for this payment.
168
        settle, failure := m.TerminalInfo()
720✔
169

720✔
170
        // Now determine the payment's status.
720✔
171
        status, err := decidePaymentStatus(m.HTLCs, m.FailureReason)
720✔
172
        if err != nil {
720✔
NEW
173
                return err
×
NEW
174
        }
×
175

176
        // Update the payment state and status.
177
        m.State = &MPPaymentState{
720✔
178
                NumAttemptsInFlight: len(m.InFlightHTLCs()),
720✔
179
                RemainingAmt:        totalAmt - sentAmt,
720✔
180
                FeesPaid:            fees,
720✔
181
                HasSettledHTLC:      settle != nil,
720✔
182
                PaymentFailed:       failure != nil,
720✔
183
        }
720✔
184
        m.Status = status
720✔
185

720✔
186
        return nil
720✔
187
}
188

189
// SetState calls the internal method setState. This is a temporary method
190
// to be used by the tests in routing. Once the tests are updated to use mocks,
191
// this method can be removed.
192
//
193
// TODO(yy): delete.
194
func (m *MPPayment) SetState() error {
717✔
195
        return m.setState()
717✔
196
}
717✔
197

198
// NeedWaitAttempts decides whether we need to hold creating more HTLC attempts
199
// and wait for the results of the payment's inflight HTLCs. Return an error if
200
// the payment is in an unexpected state.
201
func (m *MPPayment) NeedWaitAttempts() (bool, error) {
48✔
202
        // Check when the remainingAmt is not zero, which means we have more
48✔
203
        // money to be sent.
48✔
204
        if m.State.RemainingAmt != 0 {
60✔
205
                switch m.Status {
12✔
206
                // If the payment is newly created, no need to wait for HTLC
207
                // results.
208
                case StatusInitiated:
1✔
209
                        return false, nil
1✔
210

211
                // If we have inflight HTLCs, we'll check if we have terminal
212
                // states to decide if we need to wait.
213
                case StatusInFlight:
3✔
214
                        // We still have money to send, and one of the HTLCs is
3✔
215
                        // settled. We'd stop sending money and wait for all
3✔
216
                        // inflight HTLC attempts to finish.
3✔
217
                        if m.State.HasSettledHTLC {
4✔
218
                                log.Warnf("payment=%v has remaining amount "+
1✔
219
                                        "%v, yet at least one of its HTLCs is "+
1✔
220
                                        "settled", m.Info.PaymentIdentifier,
1✔
221
                                        m.State.RemainingAmt)
1✔
222

1✔
223
                                return true, nil
1✔
224
                        }
1✔
225

226
                        // The payment has a failure reason though we still
227
                        // have money to send, we'd stop sending money and wait
228
                        // for all inflight HTLC attempts to finish.
229
                        if m.State.PaymentFailed {
3✔
230
                                return true, nil
1✔
231
                        }
1✔
232

233
                        // Otherwise we don't need to wait for inflight HTLCs
234
                        // since we still have money to be sent.
235
                        return false, nil
1✔
236

237
                // We need to send more money, yet the payment is already
238
                // succeeded. Return an error in this case as the receiver is
239
                // violating the protocol.
240
                case StatusSucceeded:
1✔
241
                        return false, fmt.Errorf("%w: parts of the payment "+
1✔
242
                                "already succeeded but still have remaining "+
1✔
243
                                "amount %v", ErrPaymentInternal,
1✔
244
                                m.State.RemainingAmt)
1✔
245

246
                // The payment is failed and we have no inflight HTLCs, no need
247
                // to wait.
248
                case StatusFailed:
6✔
249
                        return false, nil
6✔
250

251
                // Unknown payment status.
252
                default:
1✔
253
                        return false, fmt.Errorf("%w: %s",
1✔
254
                                ErrUnknownPaymentStatus, m.Status)
1✔
255
                }
256
        }
257

258
        // Now we determine whether we need to wait when the remainingAmt is
259
        // already zero.
260
        switch m.Status {
39✔
261
        // When the payment is newly created, yet the payment has no remaining
262
        // amount, return an error.
263
        case StatusInitiated:
1✔
264
                return false, fmt.Errorf("%w: %v", ErrPaymentInternal, m.Status)
1✔
265

266
        // If the payment is inflight, we must wait.
267
        //
268
        // NOTE: an edge case is when all HTLCs are failed while the payment is
269
        // not failed we'd still be in this inflight state. However, since the
270
        // remainingAmt is zero here, it means we cannot be in that state as
271
        // otherwise the remainingAmt would not be zero.
272
        case StatusInFlight:
25✔
273
                return true, nil
25✔
274

275
        // If the payment is already succeeded, no need to wait.
276
        case StatusSucceeded:
14✔
277
                return false, nil
14✔
278

279
        // If the payment is already failed, yet the remaining amount is zero,
280
        // return an error as this indicates an error state. We will only each
281
        // this status when there are no inflight HTLCs and the payment is
282
        // marked as failed with a reason, which means the remainingAmt must
283
        // not be zero because our sentAmt is zero.
284
        case StatusFailed:
1✔
285
                return false, fmt.Errorf("%w: %v", ErrPaymentInternal, m.Status)
1✔
286

287
        // Unknown payment status.
288
        default:
1✔
289
                return false, fmt.Errorf("%w: %s", ErrUnknownPaymentStatus,
1✔
290
                        m.Status)
1✔
291
        }
292
}
293

294
// GetState returns the internal state of the payment.
295
func (m *MPPayment) GetState() *MPPaymentState {
60✔
296
        return m.State
60✔
297
}
60✔
298

299
// GetStatus returns the current status of the payment.
300
func (m *MPPayment) GetStatus() PaymentStatus {
161✔
301
        return m.Status
161✔
302
}
161✔
303

304
// GetHTLCs returns all the HTLCs for this payment.
305
func (m *MPPayment) GetHTLCs() []HTLCAttempt {
1✔
306
        return m.HTLCs
1✔
307
}
1✔
308

309
// AllowMoreAttempts is used to decide whether we can safely attempt more HTLCs
310
// for a given payment state. Return an error if the payment is in an
311
// unexpected state.
312
func (m *MPPayment) AllowMoreAttempts() (bool, error) {
75✔
313
        // Now check whether the remainingAmt is zero or not. If we don't have
75✔
314
        // any remainingAmt, no more HTLCs should be made.
75✔
315
        if m.State.RemainingAmt == 0 {
116✔
316
                // If the payment is newly created, yet we don't have any
41✔
317
                // remainingAmt, return an error.
41✔
318
                if m.Status == StatusInitiated {
42✔
319
                        return false, fmt.Errorf("%w: initiated payment has "+
1✔
320
                                "zero remainingAmt", ErrPaymentInternal)
1✔
321
                }
1✔
322

323
                // Otherwise, exit early since all other statuses with zero
324
                // remainingAmt indicate no more HTLCs can be made.
325
                return false, nil
40✔
326
        }
327

328
        // Otherwise, the remaining amount is not zero, we now decide whether
329
        // to make more attempts based on the payment's current status.
330
        //
331
        // If at least one of the payment's attempts is settled, yet we haven't
332
        // sent all the amount, it indicates something is wrong with the peer
333
        // as the preimage is received. In this case, return an error state.
334
        if m.Status == StatusSucceeded {
38✔
335
                return false, fmt.Errorf("%w: payment already succeeded but "+
1✔
336
                        "still have remaining amount %v", ErrPaymentInternal,
1✔
337
                        m.State.RemainingAmt)
1✔
338
        }
1✔
339

340
        // Now check if we can register a new HTLC.
341
        err := m.Registrable()
36✔
342
        if err != nil {
46✔
343
                log.Warnf("Payment(%v): cannot register HTLC attempt: %v, "+
10✔
344
                        "current status: %s", m.Info.PaymentIdentifier,
10✔
345
                        err, m.Status)
10✔
346

10✔
347
                return false, nil
10✔
348
        }
10✔
349

350
        // Now we know we can register new HTLCs.
351
        return true, nil
29✔
352
}
353

354
// MPPaymentState wraps a series of info needed for a given payment, which is
355
// used by both MPP and AMP. This is a memory representation of the payment's
356
// current state and is updated whenever the payment is read from disk.
357
type MPPaymentState struct {
358
        // NumAttemptsInFlight specifies the number of HTLCs the payment is
359
        // waiting results for.
360
        NumAttemptsInFlight int
361

362
        // RemainingAmt specifies how much more money to be sent.
363
        RemainingAmt lnwire.MilliSatoshi
364

365
        // FeesPaid specifies the total fees paid so far that can be used to
366
        // calculate remaining fee budget.
367
        FeesPaid lnwire.MilliSatoshi
368

369
        // HasSettledHTLC is true if at least one of the payment's HTLCs is
370
        // settled.
371
        HasSettledHTLC bool
372

373
        // PaymentFailed is true if the payment has been marked as failed with
374
        // a reason.
375
        PaymentFailed bool
376
}
377

378
// PaymentCreationInfo is the information necessary to have ready when
379
// initiating a payment, moving it into state InFlight.
380
type PaymentCreationInfo struct {
381
        // PaymentIdentifier is the hash this payment is paying to in case of
382
        // non-AMP payments, and the SetID for AMP payments.
383
        PaymentIdentifier lntypes.Hash
384

385
        // Value is the amount we are paying.
386
        Value lnwire.MilliSatoshi
387

388
        // CreationTime is the time when this payment was initiated.
389
        CreationTime time.Time
390

391
        // PaymentRequest is the full payment request, if any.
392
        PaymentRequest []byte
393

394
        // FirstHopCustomRecords are the TLV records that are to be sent to the
395
        // first hop of this payment. These records will be transmitted via the
396
        // wire message only and therefore do not affect the onion payload size.
397
        FirstHopCustomRecords lnwire.CustomRecords
398
}
399

400
// HTLCAttemptInfo contains static information about a specific HTLC attempt
401
// for a payment. This information is used by the router to handle any errors
402
// coming back after an attempt is made, and to query the switch about the
403
// status of the attempt.
404
type HTLCAttemptInfo struct {
405
        // AttemptID is the unique ID used for this attempt.
406
        AttemptID uint64
407

408
        // sessionKey is the raw bytes ephemeral key used for this attempt.
409
        // These bytes are lazily read off disk to save ourselves the expensive
410
        // EC operations used by btcec.PrivKeyFromBytes.
411
        sessionKey [btcec.PrivKeyBytesLen]byte
412

413
        // cachedSessionKey is our fully deserialized sesionKey. This value
414
        // may be nil if the attempt has just been read from disk and its
415
        // session key has not been used yet.
416
        cachedSessionKey *btcec.PrivateKey
417

418
        // Route is the route attempted to send the HTLC.
419
        Route route.Route
420

421
        // AttemptTime is the time at which this HTLC was attempted.
422
        AttemptTime time.Time
423

424
        // Hash is the hash used for this single HTLC attempt. For AMP payments
425
        // this will differ across attempts, for non-AMP payments each attempt
426
        // will use the same hash. This can be nil for older payment attempts,
427
        // in which the payment's PaymentHash in the PaymentCreationInfo should
428
        // be used.
429
        Hash *lntypes.Hash
430

431
        // onionBlob is the cached value for onion blob created from the sphinx
432
        // construction.
433
        onionBlob [lnwire.OnionPacketSize]byte
434

435
        // circuit is the cached value for sphinx circuit.
436
        circuit *sphinx.Circuit
437
}
438

439
// NewHtlcAttempt creates a htlc attempt.
440
func NewHtlcAttempt(attemptID uint64, sessionKey *btcec.PrivateKey,
441
        route route.Route, attemptTime time.Time,
442
        hash *lntypes.Hash) (*HTLCAttempt, error) {
206✔
443

206✔
444
        var scratch [btcec.PrivKeyBytesLen]byte
206✔
445
        copy(scratch[:], sessionKey.Serialize())
206✔
446

206✔
447
        info := HTLCAttemptInfo{
206✔
448
                AttemptID:        attemptID,
206✔
449
                sessionKey:       scratch,
206✔
450
                cachedSessionKey: sessionKey,
206✔
451
                Route:            route,
206✔
452
                AttemptTime:      attemptTime,
206✔
453
                Hash:             hash,
206✔
454
        }
206✔
455

206✔
456
        if err := info.AttachOnionBlobAndCircuit(); err != nil {
207✔
457
                return nil, err
1✔
458
        }
1✔
459

460
        return &HTLCAttempt{HTLCAttemptInfo: info}, nil
205✔
461
}
462

463
// SessionKey returns the ephemeral key used for a htlc attempt. This function
464
// performs expensive ec-ops to obtain the session key if it is not cached.
465
func (h *HTLCAttemptInfo) SessionKey() *btcec.PrivateKey {
291✔
466
        if h.cachedSessionKey == nil {
305✔
467
                h.cachedSessionKey, _ = btcec.PrivKeyFromBytes(
14✔
468
                        h.sessionKey[:],
14✔
469
                )
14✔
470
        }
14✔
471

472
        return h.cachedSessionKey
291✔
473
}
474

475
// SetSessionKey sets the session key for the HTLC attempt.
476
func (h *HTLCAttemptInfo) SetSessionKey(key [32]byte) {
707✔
477
        h.sessionKey = key
707✔
478
        h.cachedSessionKey = nil
707✔
479
}
707✔
480

481
// OnionBlob returns the onion blob created from the sphinx construction.
482
func (h *HTLCAttemptInfo) OnionBlob() ([lnwire.OnionPacketSize]byte, error) {
37✔
483
        var zeroBytes [lnwire.OnionPacketSize]byte
37✔
484
        if h.onionBlob == zeroBytes {
37✔
NEW
485
                if err := h.AttachOnionBlobAndCircuit(); err != nil {
×
NEW
486
                        return zeroBytes, err
×
NEW
487
                }
×
488
        }
489

490
        return h.onionBlob, nil
37✔
491
}
492

493
// Circuit returns the sphinx circuit for this attempt.
494
func (h *HTLCAttemptInfo) Circuit() (*sphinx.Circuit, error) {
38✔
495
        if h.circuit == nil {
50✔
496
                if err := h.AttachOnionBlobAndCircuit(); err != nil {
12✔
NEW
497
                        return nil, err
×
NEW
498
                }
×
499
        }
500

501
        return h.circuit, nil
38✔
502
}
503

504
// AttachOnionBlobAndCircuit creates a sphinx packet and caches the onion blob
505
// and circuit for this attempt.
506
func (h *HTLCAttemptInfo) AttachOnionBlobAndCircuit() error {
216✔
507
        onionBlob, circuit, err := generateSphinxPacket(
216✔
508
                &h.Route, h.Hash[:], h.SessionKey(),
216✔
509
        )
216✔
510
        if err != nil {
217✔
511
                return err
1✔
512
        }
1✔
513

514
        copy(h.onionBlob[:], onionBlob)
215✔
515
        h.circuit = circuit
215✔
516

215✔
517
        return nil
215✔
518
}
519

520
// HTLCAttempt contains information about a specific HTLC attempt for a given
521
// payment. It contains the HTLCAttemptInfo used to send the HTLC, as well
522
// as a timestamp and any known outcome of the attempt.
523
type HTLCAttempt struct {
524
        HTLCAttemptInfo
525

526
        // Settle is the preimage of a successful payment. This serves as a
527
        // proof of payment. It will only be non-nil for settled payments.
528
        //
529
        // NOTE: Can be nil if payment is not settled.
530
        Settle *HTLCSettleInfo
531

532
        // Fail is a failure reason code indicating the reason the payment
533
        // failed. It is only non-nil for failed payments.
534
        //
535
        // NOTE: Can be nil if payment is not failed.
536
        Failure *HTLCFailInfo
537
}
538

539
// HTLCSettleInfo encapsulates the information that augments an HTLCAttempt in
540
// the event that the HTLC is successful.
541
type HTLCSettleInfo struct {
542
        // Preimage is the preimage of a successful HTLC. This serves as a proof
543
        // of payment.
544
        Preimage lntypes.Preimage
545

546
        // SettleTime is the time at which this HTLC was settled.
547
        SettleTime time.Time
548
}
549

550
// HTLCFailReason is the reason an htlc failed.
551
type HTLCFailReason byte
552

553
const (
554
        // HTLCFailUnknown is recorded for htlcs that failed with an unknown
555
        // reason.
556
        HTLCFailUnknown HTLCFailReason = 0
557

558
        // HTLCFailUnreadable is recorded for htlcs that had a failure message
559
        // that couldn't be decrypted.
560
        HTLCFailUnreadable HTLCFailReason = 1
561

562
        // HTLCFailInternal is recorded for htlcs that failed because of an
563
        // internal error.
564
        HTLCFailInternal HTLCFailReason = 2
565

566
        // HTLCFailMessage is recorded for htlcs that failed with a network
567
        // failure message.
568
        HTLCFailMessage HTLCFailReason = 3
569
)
570

571
// HTLCFailInfo encapsulates the information that augments an HTLCAttempt in the
572
// event that the HTLC fails.
573
type HTLCFailInfo struct {
574
        // FailTime is the time at which this HTLC was failed.
575
        FailTime time.Time
576

577
        // Message is the wire message that failed this HTLC. This field will be
578
        // populated when the failure reason is HTLCFailMessage.
579
        Message lnwire.FailureMessage
580

581
        // Reason is the failure reason for this HTLC.
582
        Reason HTLCFailReason
583

584
        // The position in the path of the intermediate or final node that
585
        // generated the failure message. Position zero is the sender node. This
586
        // field will be populated when the failure reason is either
587
        // HTLCFailMessage or HTLCFailUnknown.
588
        FailureSourceIndex uint32
589
}
590

591
// FailureReason encodes the reason a payment ultimately failed.
592
type FailureReason byte
593

594
const (
595
        // FailureReasonTimeout indicates that the payment did timeout before a
596
        // successful payment attempt was made.
597
        FailureReasonTimeout FailureReason = 0
598

599
        // FailureReasonNoRoute indicates no successful route to the
600
        // destination was found during path finding.
601
        FailureReasonNoRoute FailureReason = 1
602

603
        // FailureReasonError indicates that an unexpected error happened during
604
        // payment.
605
        FailureReasonError FailureReason = 2
606

607
        // FailureReasonPaymentDetails indicates that either the hash is unknown
608
        // or the final cltv delta or amount is incorrect.
609
        FailureReasonPaymentDetails FailureReason = 3
610

611
        // FailureReasonInsufficientBalance indicates that we didn't have enough
612
        // balance to complete the payment.
613
        FailureReasonInsufficientBalance FailureReason = 4
614

615
        // FailureReasonCanceled indicates that the payment was canceled by the
616
        // user.
617
        FailureReasonCanceled FailureReason = 5
618

619
        // TODO(joostjager): Add failure reasons for:
620
        // LocalLiquidityInsufficient, RemoteCapacityInsufficient.
621
)
622

623
// Error returns a human-readable error string for the FailureReason.
624
func (r FailureReason) Error() string {
43✔
625
        return r.String()
43✔
626
}
43✔
627

628
// String returns a human-readable FailureReason.
629
func (r FailureReason) String() string {
43✔
630
        switch r {
43✔
631
        case FailureReasonTimeout:
12✔
632
                return "timeout"
12✔
633
        case FailureReasonNoRoute:
11✔
634
                return "no_route"
11✔
635
        case FailureReasonError:
14✔
636
                return "error"
14✔
637
        case FailureReasonPaymentDetails:
5✔
638
                return "incorrect_payment_details"
5✔
639
        case FailureReasonInsufficientBalance:
3✔
640
                return "insufficient_balance"
3✔
641
        case FailureReasonCanceled:
4✔
642
                return "canceled"
4✔
643
        }
644

NEW
645
        return "unknown"
×
646
}
647

648
// generateSphinxPacket generates then encodes a sphinx packet which encodes
649
// the onion route specified by the passed layer 3 route. The blob returned
650
// from this function can immediately be included within an HTLC add packet to
651
// be sent to the first hop within the route.
652
func generateSphinxPacket(rt *route.Route, paymentHash []byte,
653
        sessionKey *btcec.PrivateKey) ([]byte, *sphinx.Circuit, error) {
217✔
654

217✔
655
        // Now that we know we have an actual route, we'll map the route into a
217✔
656
        // sphinx payment path which includes per-hop payloads for each hop
217✔
657
        // that give each node within the route the necessary information
217✔
658
        // (fees, CLTV value, etc.) to properly forward the payment.
217✔
659
        sphinxPath, err := rt.ToSphinxPath()
217✔
660
        if err != nil {
219✔
661
                return nil, nil, err
2✔
662
        }
2✔
663

664
        log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v",
215✔
665
                paymentHash, lnutils.NewLogClosure(func() string {
215✔
NEW
666
                        path := make(
×
NEW
667
                                []sphinx.OnionHop, sphinxPath.TrueRouteLength(),
×
NEW
668
                        )
×
NEW
669
                        for i := range path {
×
NEW
670
                                hopCopy := sphinxPath[i]
×
NEW
671
                                path[i] = hopCopy
×
NEW
672
                        }
×
673

NEW
674
                        return spew.Sdump(path)
×
675
                }),
676
        )
677

678
        // Next generate the onion routing packet which allows us to perform
679
        // privacy preserving source routing across the network.
680
        sphinxPacket, err := sphinx.NewOnionPacket(
215✔
681
                sphinxPath, sessionKey, paymentHash,
215✔
682
                sphinx.DeterministicPacketFiller,
215✔
683
        )
215✔
684
        if err != nil {
215✔
NEW
685
                return nil, nil, err
×
NEW
686
        }
×
687

688
        // Finally, encode Sphinx packet using its wire representation to be
689
        // included within the HTLC add packet.
690
        var onionBlob bytes.Buffer
215✔
691
        if err := sphinxPacket.Encode(&onionBlob); err != nil {
215✔
NEW
692
                return nil, nil, err
×
NEW
693
        }
×
694

695
        log.Tracef("Generated sphinx packet: %v",
215✔
696
                lnutils.NewLogClosure(func() string {
215✔
NEW
697
                        // We make a copy of the ephemeral key and unset the
×
NEW
698
                        // internal curve here in order to keep the logs from
×
NEW
699
                        // getting noisy.
×
NEW
700
                        key := *sphinxPacket.EphemeralKey
×
NEW
701
                        packetCopy := *sphinxPacket
×
NEW
702
                        packetCopy.EphemeralKey = &key
×
NEW
703

×
NEW
704
                        return spew.Sdump(packetCopy)
×
NEW
705
                }),
×
706
        )
707

708
        return onionBlob.Bytes(), &sphinx.Circuit{
215✔
709
                SessionKey:  sessionKey,
215✔
710
                PaymentPath: sphinxPath.NodeKeys(),
215✔
711
        }, nil
215✔
712
}
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