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

lightningnetwork / lnd / 19924300449

04 Dec 2025 09:35AM UTC coverage: 53.479% (-1.9%) from 55.404%
19924300449

Pull #10419

github

web-flow
Merge f811805c6 into 20473482d
Pull Request #10419: [docs] Document use-native-sql=true for SQL migration step 2

110496 of 206616 relevant lines covered (53.48%)

21221.61 hits per line

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

87.36
/payments/db/payment.go
1
package paymentsdb
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
        "github.com/lightningnetwork/lnd/routing/route"
16
)
17

18
// FailureReason encodes the reason a payment ultimately failed.
19
type FailureReason byte
20

21
const (
22
        // FailureReasonTimeout indicates that the payment did timeout before a
23
        // successful payment attempt was made.
24
        FailureReasonTimeout FailureReason = 0
25

26
        // FailureReasonNoRoute indicates no successful route to the
27
        // destination was found during path finding.
28
        FailureReasonNoRoute FailureReason = 1
29

30
        // FailureReasonError indicates that an unexpected error happened during
31
        // payment.
32
        FailureReasonError FailureReason = 2
33

34
        // FailureReasonPaymentDetails indicates that either the hash is unknown
35
        // or the final cltv delta or amount is incorrect.
36
        FailureReasonPaymentDetails FailureReason = 3
37

38
        // FailureReasonInsufficientBalance indicates that we didn't have enough
39
        // balance to complete the payment.
40
        FailureReasonInsufficientBalance FailureReason = 4
41

42
        // FailureReasonCanceled indicates that the payment was canceled by the
43
        // user.
44
        FailureReasonCanceled FailureReason = 5
45

46
        // TODO(joostjager): Add failure reasons for:
47
        // LocalLiquidityInsufficient, RemoteCapacityInsufficient.
48
)
49

50
// Error returns a human-readable error string for the FailureReason.
51
func (r FailureReason) Error() string {
40✔
52
        return r.String()
40✔
53
}
40✔
54

55
// String returns a human-readable FailureReason.
56
func (r FailureReason) String() string {
40✔
57
        switch r {
40✔
58
        case FailureReasonTimeout:
12✔
59
                return "timeout"
12✔
60
        case FailureReasonNoRoute:
8✔
61
                return "no_route"
8✔
62
        case FailureReasonError:
14✔
63
                return "error"
14✔
64
        case FailureReasonPaymentDetails:
2✔
65
                return "incorrect_payment_details"
2✔
66
        case FailureReasonInsufficientBalance:
×
67
                return "insufficient_balance"
×
68
        case FailureReasonCanceled:
4✔
69
                return "canceled"
4✔
70
        }
71

72
        return "unknown"
×
73
}
74

75
// PaymentCreationInfo is the information necessary to have ready when
76
// initiating a payment, moving it into state InFlight.
77
type PaymentCreationInfo struct {
78
        // PaymentIdentifier is the hash this payment is paying to in case of
79
        // non-AMP payments, and the SetID for AMP payments.
80
        PaymentIdentifier lntypes.Hash
81

82
        // Value is the amount we are paying.
83
        Value lnwire.MilliSatoshi
84

85
        // CreationTime is the time when this payment was initiated.
86
        CreationTime time.Time
87

88
        // PaymentRequest is the full payment request, if any.
89
        PaymentRequest []byte
90

91
        // FirstHopCustomRecords are the TLV records that are to be sent to the
92
        // first hop of this payment. These records will be transmitted via the
93
        // wire message (UpdateAddHTLC) only and therefore do not affect the
94
        // onion payload size.
95
        FirstHopCustomRecords lnwire.CustomRecords
96
}
97

98
// String returns a human-readable description of the payment creation info.
99
func (p *PaymentCreationInfo) String() string {
8✔
100
        return fmt.Sprintf("payment_id=%v, amount=%v, created_at=%v",
8✔
101
                p.PaymentIdentifier, p.Value, p.CreationTime)
8✔
102
}
8✔
103

104
// HTLCAttemptInfo contains static information about a specific HTLC attempt
105
// for a payment. This information is used by the router to handle any errors
106
// coming back after an attempt is made, and to query the switch about the
107
// status of the attempt.
108
type HTLCAttemptInfo struct {
109
        // AttemptID is the unique ID used for this attempt.
110
        AttemptID uint64
111

112
        // sessionKey is the raw bytes ephemeral key used for this attempt.
113
        // These bytes are lazily read off disk to save ourselves the expensive
114
        // EC operations used by btcec.PrivKeyFromBytes.
115
        sessionKey [btcec.PrivKeyBytesLen]byte
116

117
        // cachedSessionKey is our fully deserialized sesionKey. This value
118
        // may be nil if the attempt has just been read from disk and its
119
        // session key has not been used yet.
120
        cachedSessionKey *btcec.PrivateKey
121

122
        // Route is the route attempted to send the HTLC.
123
        Route route.Route
124

125
        // AttemptTime is the time at which this HTLC was attempted.
126
        AttemptTime time.Time
127

128
        // Hash is the hash used for this single HTLC attempt. For AMP payments
129
        // this will differ across attempts, for non-AMP payments each attempt
130
        // will use the same hash. This can be nil for older payment attempts,
131
        // in which the payment's PaymentHash in the PaymentCreationInfo should
132
        // be used.
133
        Hash *lntypes.Hash
134

135
        // onionBlob is the cached value for onion blob created from the sphinx
136
        // construction.
137
        onionBlob [lnwire.OnionPacketSize]byte
138

139
        // circuit is the cached value for sphinx circuit.
140
        circuit *sphinx.Circuit
141
}
142

143
// NewHtlcAttempt creates a htlc attempt.
144
func NewHtlcAttempt(attemptID uint64, sessionKey *btcec.PrivateKey,
145
        route route.Route, attemptTime time.Time,
146
        hash *lntypes.Hash) (*HTLCAttempt, error) {
203✔
147

203✔
148
        var scratch [btcec.PrivKeyBytesLen]byte
203✔
149
        copy(scratch[:], sessionKey.Serialize())
203✔
150

203✔
151
        info := HTLCAttemptInfo{
203✔
152
                AttemptID:        attemptID,
203✔
153
                sessionKey:       scratch,
203✔
154
                cachedSessionKey: sessionKey,
203✔
155
                Route:            route,
203✔
156
                AttemptTime:      attemptTime,
203✔
157
                Hash:             hash,
203✔
158
        }
203✔
159

203✔
160
        if err := info.attachOnionBlobAndCircuit(); err != nil {
204✔
161
                return nil, err
1✔
162
        }
1✔
163

164
        return &HTLCAttempt{HTLCAttemptInfo: info}, nil
202✔
165
}
166

167
// SessionKey returns the ephemeral key used for a htlc attempt. This function
168
// performs expensive ec-ops to obtain the session key if it is not cached.
169
func (h *HTLCAttemptInfo) SessionKey() *btcec.PrivateKey {
215✔
170
        if h.cachedSessionKey == nil {
226✔
171
                h.cachedSessionKey, _ = btcec.PrivKeyFromBytes(
11✔
172
                        h.sessionKey[:],
11✔
173
                )
11✔
174
        }
11✔
175

176
        return h.cachedSessionKey
215✔
177
}
178

179
// setSessionKey sets the session key for the htlc attempt.
180
//
181
// NOTE: Only used for testing.
182
//
183
//nolint:unused
184
func (h *HTLCAttemptInfo) setSessionKey(sessionKey *btcec.PrivateKey) {
×
185
        h.cachedSessionKey = sessionKey
×
186

×
187
        // Also set the session key as a raw bytes.
×
188
        var scratch [btcec.PrivKeyBytesLen]byte
×
189
        copy(scratch[:], sessionKey.Serialize())
×
190
        h.sessionKey = scratch
×
191
}
×
192

193
// OnionBlob returns the onion blob created from the sphinx construction.
194
func (h *HTLCAttemptInfo) OnionBlob() ([lnwire.OnionPacketSize]byte, error) {
34✔
195
        var zeroBytes [lnwire.OnionPacketSize]byte
34✔
196
        if h.onionBlob == zeroBytes {
34✔
197
                if err := h.attachOnionBlobAndCircuit(); err != nil {
×
198
                        return zeroBytes, err
×
199
                }
×
200
        }
201

202
        return h.onionBlob, nil
34✔
203
}
204

205
// Circuit returns the sphinx circuit for this attempt.
206
func (h *HTLCAttemptInfo) Circuit() (*sphinx.Circuit, error) {
35✔
207
        if h.circuit == nil {
44✔
208
                if err := h.attachOnionBlobAndCircuit(); err != nil {
9✔
209
                        return nil, err
×
210
                }
×
211
        }
212

213
        return h.circuit, nil
35✔
214
}
215

216
// attachOnionBlobAndCircuit creates a sphinx packet and caches the onion blob
217
// and circuit for this attempt.
218
func (h *HTLCAttemptInfo) attachOnionBlobAndCircuit() error {
213✔
219
        onionBlob, circuit, err := generateSphinxPacket(
213✔
220
                &h.Route, h.Hash[:], h.SessionKey(),
213✔
221
        )
213✔
222
        if err != nil {
214✔
223
                return err
1✔
224
        }
1✔
225

226
        copy(h.onionBlob[:], onionBlob)
212✔
227
        h.circuit = circuit
212✔
228

212✔
229
        return nil
212✔
230
}
231

232
// HTLCAttempt contains information about a specific HTLC attempt for a given
233
// payment. It contains the HTLCAttemptInfo used to send the HTLC, as well
234
// as a timestamp and any known outcome of the attempt.
235
type HTLCAttempt struct {
236
        HTLCAttemptInfo
237

238
        // Settle is the preimage of a successful payment. This serves as a
239
        // proof of payment. It will only be non-nil for settled payments.
240
        //
241
        // NOTE: Can be nil if payment is not settled.
242
        Settle *HTLCSettleInfo
243

244
        // Fail is a failure reason code indicating the reason the payment
245
        // failed. It is only non-nil for failed payments.
246
        //
247
        // NOTE: Can be nil if payment is not failed.
248
        Failure *HTLCFailInfo
249
}
250

251
// HTLCSettleInfo encapsulates the information that augments an HTLCAttempt in
252
// the event that the HTLC is successful.
253
type HTLCSettleInfo struct {
254
        // Preimage is the preimage of a successful HTLC. This serves as a proof
255
        // of payment.
256
        Preimage lntypes.Preimage
257

258
        // SettleTime is the time at which this HTLC was settled.
259
        SettleTime time.Time
260
}
261

262
// HTLCFailReason is the reason an htlc failed.
263
type HTLCFailReason byte
264

265
const (
266
        // HTLCFailUnknown is recorded for htlcs that failed with an unknown
267
        // reason.
268
        HTLCFailUnknown HTLCFailReason = 0
269

270
        // HTLCFailUnreadable is recorded for htlcs that had a failure message
271
        // that couldn't be decrypted.
272
        HTLCFailUnreadable HTLCFailReason = 1
273

274
        // HTLCFailInternal is recorded for htlcs that failed because of an
275
        // internal error.
276
        HTLCFailInternal HTLCFailReason = 2
277

278
        // HTLCFailMessage is recorded for htlcs that failed with a network
279
        // failure message.
280
        HTLCFailMessage HTLCFailReason = 3
281
)
282

283
// HTLCFailInfo encapsulates the information that augments an HTLCAttempt in the
284
// event that the HTLC fails.
285
type HTLCFailInfo struct {
286
        // FailTime is the time at which this HTLC was failed.
287
        FailTime time.Time
288

289
        // Message is the wire message that failed this HTLC. This field will be
290
        // populated when the failure reason is HTLCFailMessage.
291
        Message lnwire.FailureMessage
292

293
        // Reason is the failure reason for this HTLC.
294
        Reason HTLCFailReason
295

296
        // The position in the path of the intermediate or final node that
297
        // generated the failure message. Position zero is the sender node. This
298
        // field will be populated when the failure reason is either
299
        // HTLCFailMessage or HTLCFailUnknown.
300
        FailureSourceIndex uint32
301
}
302

303
// MPPaymentState wraps a series of info needed for a given payment, which is
304
// used by both MPP and AMP. This is a memory representation of the payment's
305
// current state and is updated whenever the payment is read from disk.
306
type MPPaymentState struct {
307
        // NumAttemptsInFlight specifies the number of HTLCs the payment is
308
        // waiting results for.
309
        NumAttemptsInFlight int
310

311
        // RemainingAmt specifies how much more money to be sent.
312
        RemainingAmt lnwire.MilliSatoshi
313

314
        // FeesPaid specifies the total fees paid so far that can be used to
315
        // calculate remaining fee budget.
316
        FeesPaid lnwire.MilliSatoshi
317

318
        // HasSettledHTLC is true if at least one of the payment's HTLCs is
319
        // settled.
320
        HasSettledHTLC bool
321

322
        // PaymentFailed is true if the payment has been marked as failed with
323
        // a reason.
324
        PaymentFailed bool
325
}
326

327
// MPPayment is a wrapper around a payment's PaymentCreationInfo and
328
// HTLCAttempts. All payments will have the PaymentCreationInfo set, any
329
// HTLCs made in attempts to be completed will populated in the HTLCs slice.
330
// Each populated HTLCAttempt represents an attempted HTLC, each of which may
331
// have the associated Settle or Fail struct populated if the HTLC is no longer
332
// in-flight.
333
type MPPayment struct {
334
        // SequenceNum is a unique identifier used to sort the payments in
335
        // order of creation.
336
        SequenceNum uint64
337

338
        // Info holds all static information about this payment, and is
339
        // populated when the payment is initiated.
340
        Info *PaymentCreationInfo
341

342
        // HTLCs holds the information about individual HTLCs that we send in
343
        // order to make the payment.
344
        HTLCs []HTLCAttempt
345

346
        // FailureReason is the failure reason code indicating the reason the
347
        // payment failed.
348
        //
349
        // NOTE: Will only be set once the daemon has given up on the payment
350
        // altogether.
351
        FailureReason *FailureReason
352

353
        // Status is the current PaymentStatus of this payment.
354
        Status PaymentStatus
355

356
        // State is the current state of the payment that holds a number of key
357
        // insights and is used to determine what to do on each payment loop
358
        // iteration.
359
        State *MPPaymentState
360
}
361

362
// Terminated returns a bool to specify whether the payment is in a terminal
363
// state.
364
func (m *MPPayment) Terminated() bool {
53✔
365
        // If the payment is in terminal state, it cannot be updated.
53✔
366
        return m.Status.updatable() != nil
53✔
367
}
53✔
368

369
// TerminalInfo returns any HTLC settle info recorded. If no settle info is
370
// recorded, any payment level failure will be returned. If neither a settle
371
// nor a failure is recorded, both return values will be nil.
372
func (m *MPPayment) TerminalInfo() (*HTLCAttempt, *FailureReason) {
738✔
373
        for _, h := range m.HTLCs {
1,487✔
374
                if h.Settle != nil {
856✔
375
                        return &h, nil
107✔
376
                }
107✔
377
        }
378

379
        return nil, m.FailureReason
631✔
380
}
381

382
// SentAmt returns the sum of sent amount and fees for HTLCs that are either
383
// settled or still in flight.
384
func (m *MPPayment) SentAmt() (lnwire.MilliSatoshi, lnwire.MilliSatoshi) {
784✔
385
        var sent, fees lnwire.MilliSatoshi
784✔
386
        for _, h := range m.HTLCs {
1,604✔
387
                if h.Failure != nil {
1,174✔
388
                        continue
354✔
389
                }
390

391
                // The attempt was not failed, meaning the amount was
392
                // potentially sent to the receiver.
393
                sent += h.Route.ReceiverAmt()
466✔
394
                fees += h.Route.TotalFees()
466✔
395
        }
396

397
        return sent, fees
784✔
398
}
399

400
// InFlightHTLCs returns the HTLCs that are still in-flight, meaning they have
401
// not been settled or failed.
402
func (m *MPPayment) InFlightHTLCs() []HTLCAttempt {
812✔
403
        var inflights []HTLCAttempt
812✔
404
        for _, h := range m.HTLCs {
1,647✔
405
                if h.Settle != nil || h.Failure != nil {
1,286✔
406
                        continue
451✔
407
                }
408

409
                inflights = append(inflights, h)
384✔
410
        }
411

412
        return inflights
812✔
413
}
414

415
// GetAttempt returns the specified htlc attempt on the payment.
416
func (m *MPPayment) GetAttempt(id uint64) (*HTLCAttempt, error) {
6✔
417
        // TODO(yy): iteration can be slow, make it into a tree or use BS.
6✔
418
        for _, htlc := range m.HTLCs {
12✔
419
                htlc := htlc
6✔
420
                if htlc.AttemptID == id {
12✔
421
                        return &htlc, nil
6✔
422
                }
6✔
423
        }
424

425
        return nil, errors.New("htlc attempt not found on payment")
×
426
}
427

428
// Registrable returns an error to specify whether adding more HTLCs to the
429
// payment with its current status is allowed. A payment can accept new HTLC
430
// registrations when it's newly created, or none of its HTLCs is in a terminal
431
// state.
432
func (m *MPPayment) Registrable() error {
127✔
433
        // If updating the payment is not allowed, we can't register new HTLCs.
127✔
434
        // Otherwise, the status must be either `StatusInitiated` or
127✔
435
        // `StatusInFlight`.
127✔
436
        if err := m.Status.updatable(); err != nil {
143✔
437
                return err
16✔
438
        }
16✔
439

440
        // Exit early if this is not inflight.
441
        if m.Status != StatusInFlight {
157✔
442
                return nil
46✔
443
        }
46✔
444

445
        // There are still inflight HTLCs and we need to check whether there
446
        // are settled HTLCs or the payment is failed. If we already have
447
        // settled HTLCs, we won't allow adding more HTLCs.
448
        if m.State.HasSettledHTLC {
72✔
449
                return ErrPaymentPendingSettled
7✔
450
        }
7✔
451

452
        // If the payment is already failed, we won't allow adding more HTLCs.
453
        if m.State.PaymentFailed {
64✔
454
                return ErrPaymentPendingFailed
6✔
455
        }
6✔
456

457
        // Otherwise we can add more HTLCs.
458
        return nil
52✔
459
}
460

461
// setState creates and attaches a new MPPaymentState to the payment. It also
462
// updates the payment's status based on its current state.
463
func (m *MPPayment) setState() error {
718✔
464
        // Fetch the total amount and fees that has already been sent in
718✔
465
        // settled and still in-flight shards.
718✔
466
        sentAmt, fees := m.SentAmt()
718✔
467

718✔
468
        // Sanity check we haven't sent a value larger than the payment amount.
718✔
469
        totalAmt := m.Info.Value
718✔
470
        if sentAmt > totalAmt {
719✔
471
                return fmt.Errorf("%w: sent=%v, total=%v",
1✔
472
                        ErrSentExceedsTotal, sentAmt, totalAmt)
1✔
473
        }
1✔
474

475
        // Get any terminal info for this payment.
476
        settle, failure := m.TerminalInfo()
717✔
477

717✔
478
        // Now determine the payment's status.
717✔
479
        status, err := decidePaymentStatus(m.HTLCs, m.FailureReason)
717✔
480
        if err != nil {
717✔
481
                return err
×
482
        }
×
483

484
        // Update the payment state and status.
485
        m.State = &MPPaymentState{
717✔
486
                NumAttemptsInFlight: len(m.InFlightHTLCs()),
717✔
487
                RemainingAmt:        totalAmt - sentAmt,
717✔
488
                FeesPaid:            fees,
717✔
489
                HasSettledHTLC:      settle != nil,
717✔
490
                PaymentFailed:       failure != nil,
717✔
491
        }
717✔
492
        m.Status = status
717✔
493

717✔
494
        return nil
717✔
495
}
496

497
// SetState calls the internal method setState. This is a temporary method
498
// to be used by the tests in routing. Once the tests are updated to use mocks,
499
// this method can be removed.
500
//
501
// TODO(yy): delete.
502
func (m *MPPayment) SetState() error {
74✔
503
        return m.setState()
74✔
504
}
74✔
505

506
// NeedWaitAttempts decides whether we need to hold creating more HTLC attempts
507
// and wait for the results of the payment's inflight HTLCs. Return an error if
508
// the payment is in an unexpected state.
509
func (m *MPPayment) NeedWaitAttempts() (bool, error) {
45✔
510
        // Check when the remainingAmt is not zero, which means we have more
45✔
511
        // money to be sent.
45✔
512
        if m.State.RemainingAmt != 0 {
54✔
513
                switch m.Status {
9✔
514
                // If the payment is newly created, no need to wait for HTLC
515
                // results.
516
                case StatusInitiated:
1✔
517
                        return false, nil
1✔
518

519
                // If we have inflight HTLCs, we'll check if we have terminal
520
                // states to decide if we need to wait.
521
                case StatusInFlight:
3✔
522
                        // We still have money to send, and one of the HTLCs is
3✔
523
                        // settled. We'd stop sending money and wait for all
3✔
524
                        // inflight HTLC attempts to finish.
3✔
525
                        if m.State.HasSettledHTLC {
4✔
526
                                log.Warnf("payment=%v has remaining amount "+
1✔
527
                                        "%v, yet at least one of its HTLCs is "+
1✔
528
                                        "settled", m.Info.PaymentIdentifier,
1✔
529
                                        m.State.RemainingAmt)
1✔
530

1✔
531
                                return true, nil
1✔
532
                        }
1✔
533

534
                        // The payment has a failure reason though we still
535
                        // have money to send, we'd stop sending money and wait
536
                        // for all inflight HTLC attempts to finish.
537
                        if m.State.PaymentFailed {
3✔
538
                                return true, nil
1✔
539
                        }
1✔
540

541
                        // Otherwise we don't need to wait for inflight HTLCs
542
                        // since we still have money to be sent.
543
                        return false, nil
1✔
544

545
                // We need to send more money, yet the payment is already
546
                // succeeded. Return an error in this case as the receiver is
547
                // violating the protocol.
548
                case StatusSucceeded:
1✔
549
                        return false, fmt.Errorf("%w: parts of the payment "+
1✔
550
                                "already succeeded but still have remaining "+
1✔
551
                                "amount %v", ErrPaymentInternal,
1✔
552
                                m.State.RemainingAmt)
1✔
553

554
                // The payment is failed and we have no inflight HTLCs, no need
555
                // to wait.
556
                case StatusFailed:
3✔
557
                        return false, nil
3✔
558

559
                // Unknown payment status.
560
                default:
1✔
561
                        return false, fmt.Errorf("%w: %s",
1✔
562
                                ErrUnknownPaymentStatus, m.Status)
1✔
563
                }
564
        }
565

566
        // Now we determine whether we need to wait when the remainingAmt is
567
        // already zero.
568
        switch m.Status {
36✔
569
        // When the payment is newly created, yet the payment has no remaining
570
        // amount, return an error.
571
        case StatusInitiated:
1✔
572
                return false, fmt.Errorf("%w: %v",
1✔
573
                        ErrPaymentInternal, m.Status)
1✔
574

575
        // If the payment is inflight, we must wait.
576
        //
577
        // NOTE: an edge case is when all HTLCs are failed while the payment is
578
        // not failed we'd still be in this inflight state. However, since the
579
        // remainingAmt is zero here, it means we cannot be in that state as
580
        // otherwise the remainingAmt would not be zero.
581
        case StatusInFlight:
22✔
582
                return true, nil
22✔
583

584
        // If the payment is already succeeded, no need to wait.
585
        case StatusSucceeded:
11✔
586
                return false, nil
11✔
587

588
        // If the payment is already failed, yet the remaining amount is zero,
589
        // return an error as this indicates an error state. We will only each
590
        // this status when there are no inflight HTLCs and the payment is
591
        // marked as failed with a reason, which means the remainingAmt must
592
        // not be zero because our sentAmt is zero.
593
        case StatusFailed:
1✔
594
                return false, fmt.Errorf("%w: %v",
1✔
595
                        ErrPaymentInternal, m.Status)
1✔
596

597
        // Unknown payment status.
598
        default:
1✔
599
                return false, fmt.Errorf("%w: %s",
1✔
600
                        ErrUnknownPaymentStatus, m.Status)
1✔
601
        }
602
}
603

604
// GetState returns the internal state of the payment.
605
func (m *MPPayment) GetState() *MPPaymentState {
57✔
606
        return m.State
57✔
607
}
57✔
608

609
// GetStatus returns the current status of the payment.
610
func (m *MPPayment) GetStatus() PaymentStatus {
158✔
611
        return m.Status
158✔
612
}
158✔
613

614
// GetHTLCs returns all the HTLCs for this payment.
615
func (m *MPPayment) GetHTLCs() []HTLCAttempt {
1✔
616
        return m.HTLCs
1✔
617
}
1✔
618

619
// AllowMoreAttempts is used to decide whether we can safely attempt more HTLCs
620
// for a given payment state. Return an error if the payment is in an
621
// unexpected state.
622
func (m *MPPayment) AllowMoreAttempts() (bool, error) {
72✔
623
        // Now check whether the remainingAmt is zero or not. If we don't have
72✔
624
        // any remainingAmt, no more HTLCs should be made.
72✔
625
        if m.State.RemainingAmt == 0 {
110✔
626
                // If the payment is newly created, yet we don't have any
38✔
627
                // remainingAmt, return an error.
38✔
628
                if m.Status == StatusInitiated {
39✔
629
                        return false, fmt.Errorf("%w: initiated payment has "+
1✔
630
                                "zero remainingAmt",
1✔
631
                                ErrPaymentInternal)
1✔
632
                }
1✔
633

634
                // Otherwise, exit early since all other statuses with zero
635
                // remainingAmt indicate no more HTLCs can be made.
636
                return false, nil
37✔
637
        }
638

639
        // Otherwise, the remaining amount is not zero, we now decide whether
640
        // to make more attempts based on the payment's current status.
641
        //
642
        // If at least one of the payment's attempts is settled, yet we haven't
643
        // sent all the amount, it indicates something is wrong with the peer
644
        // as the preimage is received. In this case, return an error state.
645
        if m.Status == StatusSucceeded {
35✔
646
                return false, fmt.Errorf("%w: payment already succeeded but "+
1✔
647
                        "still have remaining amount %v",
1✔
648
                        ErrPaymentInternal, m.State.RemainingAmt)
1✔
649
        }
1✔
650

651
        // Now check if we can register a new HTLC.
652
        err := m.Registrable()
33✔
653
        if err != nil {
40✔
654
                log.Warnf("Payment(%v): cannot register HTLC attempt: %v, "+
7✔
655
                        "current status: %s", m.Info.PaymentIdentifier,
7✔
656
                        err, m.Status)
7✔
657

7✔
658
                return false, nil
7✔
659
        }
7✔
660

661
        // Now we know we can register new HTLCs.
662
        return true, nil
26✔
663
}
664

665
// generateSphinxPacket generates then encodes a sphinx packet which encodes
666
// the onion route specified by the passed layer 3 route. The blob returned
667
// from this function can immediately be included within an HTLC add packet to
668
// be sent to the first hop within the route.
669
func generateSphinxPacket(rt *route.Route, paymentHash []byte,
670
        sessionKey *btcec.PrivateKey) ([]byte, *sphinx.Circuit, error) {
214✔
671

214✔
672
        // Now that we know we have an actual route, we'll map the route into a
214✔
673
        // sphinx payment path which includes per-hop payloads for each hop
214✔
674
        // that give each node within the route the necessary information
214✔
675
        // (fees, CLTV value, etc.) to properly forward the payment.
214✔
676
        sphinxPath, err := rt.ToSphinxPath()
214✔
677
        if err != nil {
216✔
678
                return nil, nil, err
2✔
679
        }
2✔
680

681
        log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v",
212✔
682
                paymentHash, lnutils.NewLogClosure(func() string {
212✔
683
                        path := make(
×
684
                                []sphinx.OnionHop, sphinxPath.TrueRouteLength(),
×
685
                        )
×
686
                        for i := range path {
×
687
                                hopCopy := sphinxPath[i]
×
688
                                path[i] = hopCopy
×
689
                        }
×
690

691
                        return spew.Sdump(path)
×
692
                }),
693
        )
694

695
        // Next generate the onion routing packet which allows us to perform
696
        // privacy preserving source routing across the network.
697
        sphinxPacket, err := sphinx.NewOnionPacket(
212✔
698
                sphinxPath, sessionKey, paymentHash,
212✔
699
                sphinx.DeterministicPacketFiller,
212✔
700
        )
212✔
701
        if err != nil {
212✔
702
                return nil, nil, err
×
703
        }
×
704

705
        // Finally, encode Sphinx packet using its wire representation to be
706
        // included within the HTLC add packet.
707
        var onionBlob bytes.Buffer
212✔
708
        if err := sphinxPacket.Encode(&onionBlob); err != nil {
212✔
709
                return nil, nil, err
×
710
        }
×
711

712
        log.Tracef("Generated sphinx packet: %v",
212✔
713
                lnutils.NewLogClosure(func() string {
212✔
714
                        // We make a copy of the ephemeral key and unset the
×
715
                        // internal curve here in order to keep the logs from
×
716
                        // getting noisy.
×
717
                        key := *sphinxPacket.EphemeralKey
×
718
                        packetCopy := *sphinxPacket
×
719
                        packetCopy.EphemeralKey = &key
×
720

×
721
                        return spew.Sdump(packetCopy)
×
722
                }),
×
723
        )
724

725
        return onionBlob.Bytes(), &sphinx.Circuit{
212✔
726
                SessionKey:  sessionKey,
212✔
727
                PaymentPath: sphinxPath.NodeKeys(),
212✔
728
        }, nil
212✔
729
}
730

731
// verifyAttempt validates that a new HTLC attempt is compatible with the
732
// existing payment and its in-flight HTLCs. This function checks:
733
//  1. MPP (Multi-Path Payment) compatibility between attempts
734
//  2. Blinded payment consistency
735
//  3. Amount validation
736
//  4. Total payment amount limits
737
func verifyAttempt(payment *MPPayment, attempt *HTLCAttemptInfo) error {
84✔
738
        // If the final hop has encrypted data, then we know this is a
84✔
739
        // blinded payment. In blinded payments, MPP records are not set
84✔
740
        // for split payments and the recipient is responsible for using
84✔
741
        // a consistent PathID across the various encrypted data
84✔
742
        // payloads that we received from them for this payment. All we
84✔
743
        // need to check is that the total amount field for each HTLC
84✔
744
        // in the split payment is correct.
84✔
745
        isBlinded := len(attempt.Route.FinalHop().EncryptedData) != 0
84✔
746

84✔
747
        // Make sure any existing shards match the new one with regards
84✔
748
        // to MPP options.
84✔
749
        mpp := attempt.Route.FinalHop().MPP
84✔
750

84✔
751
        // MPP records should not be set for attempts to blinded paths.
84✔
752
        if isBlinded && mpp != nil {
85✔
753
                return ErrMPPRecordInBlindedPayment
1✔
754
        }
1✔
755

756
        for _, h := range payment.InFlightHTLCs() {
138✔
757
                hMpp := h.Route.FinalHop().MPP
55✔
758
                hBlinded := len(h.Route.FinalHop().EncryptedData) != 0
55✔
759

55✔
760
                // If this is a blinded payment, then no existing HTLCs
55✔
761
                // should have MPP records.
55✔
762
                if isBlinded && hMpp != nil {
55✔
763
                        return ErrMPPRecordInBlindedPayment
×
764
                }
×
765

766
                // If the payment is blinded (previous attempts used blinded
767
                // paths) and the attempt is not, or vice versa, return an
768
                // error.
769
                if isBlinded != hBlinded {
58✔
770
                        return ErrMixedBlindedAndNonBlindedPayments
3✔
771
                }
3✔
772

773
                // If this is a blinded payment, then we just need to
774
                // check that the TotalAmtMsat field for this shard
775
                // is equal to that of any other shard in the same
776
                // payment.
777
                if isBlinded {
54✔
778
                        if attempt.Route.FinalHop().TotalAmtMsat !=
2✔
779
                                h.Route.FinalHop().TotalAmtMsat {
3✔
780

1✔
781
                                return ErrBlindedPaymentTotalAmountMismatch
1✔
782
                        }
1✔
783

784
                        continue
1✔
785
                }
786

787
                switch {
50✔
788
                // We tried to register a non-MPP attempt for a MPP
789
                // payment.
790
                case mpp == nil && hMpp != nil:
3✔
791
                        return ErrMPPayment
3✔
792

793
                // We tried to register a MPP shard for a non-MPP
794
                // payment.
795
                case mpp != nil && hMpp == nil:
3✔
796
                        return ErrNonMPPayment
3✔
797

798
                // Non-MPP payment, nothing more to validate.
799
                case mpp == nil:
×
800
                        continue
×
801
                }
802

803
                // Check that MPP options match.
804
                if mpp.PaymentAddr() != hMpp.PaymentAddr() {
47✔
805
                        return ErrMPPPaymentAddrMismatch
3✔
806
                }
3✔
807

808
                if mpp.TotalMsat() != hMpp.TotalMsat() {
44✔
809
                        return ErrMPPTotalAmountMismatch
3✔
810
                }
3✔
811
        }
812

813
        // If this is a non-MPP attempt, it must match the total amount
814
        // exactly. Note that a blinded payment is considered an MPP
815
        // attempt.
816
        amt := attempt.Route.ReceiverAmt()
67✔
817
        if !isBlinded && mpp == nil && amt != payment.Info.Value {
68✔
818
                return ErrValueMismatch
1✔
819
        }
1✔
820

821
        // Ensure we aren't sending more than the total payment amount.
822
        sentAmt, _ := payment.SentAmt()
66✔
823
        if sentAmt+amt > payment.Info.Value {
75✔
824
                return fmt.Errorf("%w: attempted=%v, payment amount=%v",
9✔
825
                        ErrValueExceedsAmt, sentAmt+amt, payment.Info.Value)
9✔
826
        }
9✔
827

828
        return nil
57✔
829
}
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