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

lightningnetwork / lnd / 17027244024

17 Aug 2025 11:32PM UTC coverage: 57.287% (-9.5%) from 66.765%
17027244024

Pull #10167

github

web-flow
Merge fcb4f4303 into fb1adfc21
Pull Request #10167: multi: bump Go to 1.24.6

3 of 18 new or added lines in 6 files covered. (16.67%)

28537 existing lines in 457 files now uncovered.

99094 of 172978 relevant lines covered (57.29%)

1.78 hits per line

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

64.22
/payments/db/payment.go
1
package paymentsdb
2

3
import (
4
        "bytes"
5
        "errors"
6
        "fmt"
7
        "io"
8
        "math"
9
        "time"
10

11
        "github.com/btcsuite/btcd/btcec/v2"
12
        "github.com/btcsuite/btcd/wire"
13
        "github.com/davecgh/go-spew/spew"
14
        sphinx "github.com/lightningnetwork/lightning-onion"
15
        "github.com/lightningnetwork/lnd/lntypes"
16
        "github.com/lightningnetwork/lnd/lnutils"
17
        "github.com/lightningnetwork/lnd/lnwire"
18
        "github.com/lightningnetwork/lnd/routing/route"
19
)
20

21
// FailureReason encodes the reason a payment ultimately failed.
22
type FailureReason byte
23

24
const (
25
        // FailureReasonTimeout indicates that the payment did timeout before a
26
        // successful payment attempt was made.
27
        FailureReasonTimeout FailureReason = 0
28

29
        // FailureReasonNoRoute indicates no successful route to the
30
        // destination was found during path finding.
31
        FailureReasonNoRoute FailureReason = 1
32

33
        // FailureReasonError indicates that an unexpected error happened during
34
        // payment.
35
        FailureReasonError FailureReason = 2
36

37
        // FailureReasonPaymentDetails indicates that either the hash is unknown
38
        // or the final cltv delta or amount is incorrect.
39
        FailureReasonPaymentDetails FailureReason = 3
40

41
        // FailureReasonInsufficientBalance indicates that we didn't have enough
42
        // balance to complete the payment.
43
        FailureReasonInsufficientBalance FailureReason = 4
44

45
        // FailureReasonCanceled indicates that the payment was canceled by the
46
        // user.
47
        FailureReasonCanceled FailureReason = 5
48

49
        // TODO(joostjager): Add failure reasons for:
50
        // LocalLiquidityInsufficient, RemoteCapacityInsufficient.
51
)
52

53
// Error returns a human-readable error string for the FailureReason.
54
func (r FailureReason) Error() string {
3✔
55
        return r.String()
3✔
56
}
3✔
57

58
// String returns a human-readable FailureReason.
59
func (r FailureReason) String() string {
3✔
60
        switch r {
3✔
UNCOV
61
        case FailureReasonTimeout:
×
UNCOV
62
                return "timeout"
×
63
        case FailureReasonNoRoute:
3✔
64
                return "no_route"
3✔
UNCOV
65
        case FailureReasonError:
×
UNCOV
66
                return "error"
×
67
        case FailureReasonPaymentDetails:
3✔
68
                return "incorrect_payment_details"
3✔
69
        case FailureReasonInsufficientBalance:
3✔
70
                return "insufficient_balance"
3✔
71
        case FailureReasonCanceled:
3✔
72
                return "canceled"
3✔
73
        }
74

75
        return "unknown"
×
76
}
77

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

85
        // Value is the amount we are paying.
86
        Value lnwire.MilliSatoshi
87

88
        // CreationTime is the time when this payment was initiated.
89
        CreationTime time.Time
90

91
        // PaymentRequest is the full payment request, if any.
92
        PaymentRequest []byte
93

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

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

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

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

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

124
        // Route is the route attempted to send the HTLC.
125
        Route route.Route
126

127
        // AttemptTime is the time at which this HTLC was attempted.
128
        AttemptTime time.Time
129

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

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

141
        // circuit is the cached value for sphinx circuit.
142
        circuit *sphinx.Circuit
143
}
144

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

3✔
150
        var scratch [btcec.PrivKeyBytesLen]byte
3✔
151
        copy(scratch[:], sessionKey.Serialize())
3✔
152

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

3✔
162
        if err := info.attachOnionBlobAndCircuit(); err != nil {
3✔
UNCOV
163
                return nil, err
×
UNCOV
164
        }
×
165

166
        return &HTLCAttempt{HTLCAttemptInfo: info}, nil
3✔
167
}
168

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

178
        return h.cachedSessionKey
3✔
179
}
180

181
// OnionBlob returns the onion blob created from the sphinx construction.
182
func (h *HTLCAttemptInfo) OnionBlob() ([lnwire.OnionPacketSize]byte, error) {
3✔
183
        var zeroBytes [lnwire.OnionPacketSize]byte
3✔
184
        if h.onionBlob == zeroBytes {
3✔
185
                if err := h.attachOnionBlobAndCircuit(); err != nil {
×
186
                        return zeroBytes, err
×
187
                }
×
188
        }
189

190
        return h.onionBlob, nil
3✔
191
}
192

193
// Circuit returns the sphinx circuit for this attempt.
194
func (h *HTLCAttemptInfo) Circuit() (*sphinx.Circuit, error) {
3✔
195
        if h.circuit == nil {
6✔
196
                if err := h.attachOnionBlobAndCircuit(); err != nil {
3✔
197
                        return nil, err
×
198
                }
×
199
        }
200

201
        return h.circuit, nil
3✔
202
}
203

204
// attachOnionBlobAndCircuit creates a sphinx packet and caches the onion blob
205
// and circuit for this attempt.
206
func (h *HTLCAttemptInfo) attachOnionBlobAndCircuit() error {
3✔
207
        onionBlob, circuit, err := generateSphinxPacket(
3✔
208
                &h.Route, h.Hash[:], h.SessionKey(),
3✔
209
        )
3✔
210
        if err != nil {
3✔
UNCOV
211
                return err
×
UNCOV
212
        }
×
213

214
        copy(h.onionBlob[:], onionBlob)
3✔
215
        h.circuit = circuit
3✔
216

3✔
217
        return nil
3✔
218
}
219

220
// HTLCAttempt contains information about a specific HTLC attempt for a given
221
// payment. It contains the HTLCAttemptInfo used to send the HTLC, as well
222
// as a timestamp and any known outcome of the attempt.
223
type HTLCAttempt struct {
224
        HTLCAttemptInfo
225

226
        // Settle is the preimage of a successful payment. This serves as a
227
        // proof of payment. It will only be non-nil for settled payments.
228
        //
229
        // NOTE: Can be nil if payment is not settled.
230
        Settle *HTLCSettleInfo
231

232
        // Fail is a failure reason code indicating the reason the payment
233
        // failed. It is only non-nil for failed payments.
234
        //
235
        // NOTE: Can be nil if payment is not failed.
236
        Failure *HTLCFailInfo
237
}
238

239
// HTLCSettleInfo encapsulates the information that augments an HTLCAttempt in
240
// the event that the HTLC is successful.
241
type HTLCSettleInfo struct {
242
        // Preimage is the preimage of a successful HTLC. This serves as a proof
243
        // of payment.
244
        Preimage lntypes.Preimage
245

246
        // SettleTime is the time at which this HTLC was settled.
247
        SettleTime time.Time
248
}
249

250
// HTLCFailReason is the reason an htlc failed.
251
type HTLCFailReason byte
252

253
const (
254
        // HTLCFailUnknown is recorded for htlcs that failed with an unknown
255
        // reason.
256
        HTLCFailUnknown HTLCFailReason = 0
257

258
        // HTLCFailUnreadable is recorded for htlcs that had a failure message
259
        // that couldn't be decrypted.
260
        HTLCFailUnreadable HTLCFailReason = 1
261

262
        // HTLCFailInternal is recorded for htlcs that failed because of an
263
        // internal error.
264
        HTLCFailInternal HTLCFailReason = 2
265

266
        // HTLCFailMessage is recorded for htlcs that failed with a network
267
        // failure message.
268
        HTLCFailMessage HTLCFailReason = 3
269
)
270

271
// HTLCFailInfo encapsulates the information that augments an HTLCAttempt in the
272
// event that the HTLC fails.
273
type HTLCFailInfo struct {
274
        // FailTime is the time at which this HTLC was failed.
275
        FailTime time.Time
276

277
        // Message is the wire message that failed this HTLC. This field will be
278
        // populated when the failure reason is HTLCFailMessage.
279
        Message lnwire.FailureMessage
280

281
        // Reason is the failure reason for this HTLC.
282
        Reason HTLCFailReason
283

284
        // The position in the path of the intermediate or final node that
285
        // generated the failure message. Position zero is the sender node. This
286
        // field will be populated when the failure reason is either
287
        // HTLCFailMessage or HTLCFailUnknown.
288
        FailureSourceIndex uint32
289
}
290

291
// MPPaymentState wraps a series of info needed for a given payment, which is
292
// used by both MPP and AMP. This is a memory representation of the payment's
293
// current state and is updated whenever the payment is read from disk.
294
type MPPaymentState struct {
295
        // NumAttemptsInFlight specifies the number of HTLCs the payment is
296
        // waiting results for.
297
        NumAttemptsInFlight int
298

299
        // RemainingAmt specifies how much more money to be sent.
300
        RemainingAmt lnwire.MilliSatoshi
301

302
        // FeesPaid specifies the total fees paid so far that can be used to
303
        // calculate remaining fee budget.
304
        FeesPaid lnwire.MilliSatoshi
305

306
        // HasSettledHTLC is true if at least one of the payment's HTLCs is
307
        // settled.
308
        HasSettledHTLC bool
309

310
        // PaymentFailed is true if the payment has been marked as failed with
311
        // a reason.
312
        PaymentFailed bool
313
}
314

315
// MPPayment is a wrapper around a payment's PaymentCreationInfo and
316
// HTLCAttempts. All payments will have the PaymentCreationInfo set, any
317
// HTLCs made in attempts to be completed will populated in the HTLCs slice.
318
// Each populated HTLCAttempt represents an attempted HTLC, each of which may
319
// have the associated Settle or Fail struct populated if the HTLC is no longer
320
// in-flight.
321
type MPPayment struct {
322
        // SequenceNum is a unique identifier used to sort the payments in
323
        // order of creation.
324
        SequenceNum uint64
325

326
        // Info holds all static information about this payment, and is
327
        // populated when the payment is initiated.
328
        Info *PaymentCreationInfo
329

330
        // HTLCs holds the information about individual HTLCs that we send in
331
        // order to make the payment.
332
        HTLCs []HTLCAttempt
333

334
        // FailureReason is the failure reason code indicating the reason the
335
        // payment failed.
336
        //
337
        // NOTE: Will only be set once the daemon has given up on the payment
338
        // altogether.
339
        FailureReason *FailureReason
340

341
        // Status is the current PaymentStatus of this payment.
342
        Status PaymentStatus
343

344
        // State is the current state of the payment that holds a number of key
345
        // insights and is used to determine what to do on each payment loop
346
        // iteration.
347
        State *MPPaymentState
348
}
349

350
// Terminated returns a bool to specify whether the payment is in a terminal
351
// state.
352
func (m *MPPayment) Terminated() bool {
3✔
353
        // If the payment is in terminal state, it cannot be updated.
3✔
354
        return m.Status.updatable() != nil
3✔
355
}
3✔
356

357
// TerminalInfo returns any HTLC settle info recorded. If no settle info is
358
// recorded, any payment level failure will be returned. If neither a settle
359
// nor a failure is recorded, both return values will be nil.
360
func (m *MPPayment) TerminalInfo() (*HTLCAttempt, *FailureReason) {
3✔
361
        for _, h := range m.HTLCs {
6✔
362
                if h.Settle != nil {
6✔
363
                        return &h, nil
3✔
364
                }
3✔
365
        }
366

367
        return nil, m.FailureReason
3✔
368
}
369

370
// SentAmt returns the sum of sent amount and fees for HTLCs that are either
371
// settled or still in flight.
372
func (m *MPPayment) SentAmt() (lnwire.MilliSatoshi, lnwire.MilliSatoshi) {
3✔
373
        var sent, fees lnwire.MilliSatoshi
3✔
374
        for _, h := range m.HTLCs {
6✔
375
                if h.Failure != nil {
6✔
376
                        continue
3✔
377
                }
378

379
                // The attempt was not failed, meaning the amount was
380
                // potentially sent to the receiver.
381
                sent += h.Route.ReceiverAmt()
3✔
382
                fees += h.Route.TotalFees()
3✔
383
        }
384

385
        return sent, fees
3✔
386
}
387

388
// InFlightHTLCs returns the HTLCs that are still in-flight, meaning they have
389
// not been settled or failed.
390
func (m *MPPayment) InFlightHTLCs() []HTLCAttempt {
3✔
391
        var inflights []HTLCAttempt
3✔
392
        for _, h := range m.HTLCs {
6✔
393
                if h.Settle != nil || h.Failure != nil {
6✔
394
                        continue
3✔
395
                }
396

397
                inflights = append(inflights, h)
3✔
398
        }
399

400
        return inflights
3✔
401
}
402

403
// GetAttempt returns the specified htlc attempt on the payment.
404
func (m *MPPayment) GetAttempt(id uint64) (*HTLCAttempt, error) {
3✔
405
        // TODO(yy): iteration can be slow, make it into a tree or use BS.
3✔
406
        for _, htlc := range m.HTLCs {
6✔
407
                htlc := htlc
3✔
408
                if htlc.AttemptID == id {
6✔
409
                        return &htlc, nil
3✔
410
                }
3✔
411
        }
412

413
        return nil, errors.New("htlc attempt not found on payment")
×
414
}
415

416
// Registrable returns an error to specify whether adding more HTLCs to the
417
// payment with its current status is allowed. A payment can accept new HTLC
418
// registrations when it's newly created, or none of its HTLCs is in a terminal
419
// state.
420
func (m *MPPayment) Registrable() error {
3✔
421
        // If updating the payment is not allowed, we can't register new HTLCs.
3✔
422
        // Otherwise, the status must be either `StatusInitiated` or
3✔
423
        // `StatusInFlight`.
3✔
424
        if err := m.Status.updatable(); err != nil {
6✔
425
                return err
3✔
426
        }
3✔
427

428
        // Exit early if this is not inflight.
429
        if m.Status != StatusInFlight {
6✔
430
                return nil
3✔
431
        }
3✔
432

433
        // There are still inflight HTLCs and we need to check whether there
434
        // are settled HTLCs or the payment is failed. If we already have
435
        // settled HTLCs, we won't allow adding more HTLCs.
436
        if m.State.HasSettledHTLC {
3✔
UNCOV
437
                return ErrPaymentPendingSettled
×
UNCOV
438
        }
×
439

440
        // If the payment is already failed, we won't allow adding more HTLCs.
441
        if m.State.PaymentFailed {
3✔
UNCOV
442
                return ErrPaymentPendingFailed
×
UNCOV
443
        }
×
444

445
        // Otherwise we can add more HTLCs.
446
        return nil
3✔
447
}
448

449
// setState creates and attaches a new MPPaymentState to the payment. It also
450
// updates the payment's status based on its current state.
451
func (m *MPPayment) setState() error {
3✔
452
        // Fetch the total amount and fees that has already been sent in
3✔
453
        // settled and still in-flight shards.
3✔
454
        sentAmt, fees := m.SentAmt()
3✔
455

3✔
456
        // Sanity check we haven't sent a value larger than the payment amount.
3✔
457
        totalAmt := m.Info.Value
3✔
458
        if sentAmt > totalAmt {
3✔
UNCOV
459
                return fmt.Errorf("%w: sent=%v, total=%v",
×
UNCOV
460
                        ErrSentExceedsTotal, sentAmt, totalAmt)
×
UNCOV
461
        }
×
462

463
        // Get any terminal info for this payment.
464
        settle, failure := m.TerminalInfo()
3✔
465

3✔
466
        // Now determine the payment's status.
3✔
467
        status, err := decidePaymentStatus(m.HTLCs, m.FailureReason)
3✔
468
        if err != nil {
3✔
469
                return err
×
470
        }
×
471

472
        // Update the payment state and status.
473
        m.State = &MPPaymentState{
3✔
474
                NumAttemptsInFlight: len(m.InFlightHTLCs()),
3✔
475
                RemainingAmt:        totalAmt - sentAmt,
3✔
476
                FeesPaid:            fees,
3✔
477
                HasSettledHTLC:      settle != nil,
3✔
478
                PaymentFailed:       failure != nil,
3✔
479
        }
3✔
480
        m.Status = status
3✔
481

3✔
482
        return nil
3✔
483
}
484

485
// SetState calls the internal method setState. This is a temporary method
486
// to be used by the tests in routing. Once the tests are updated to use mocks,
487
// this method can be removed.
488
//
489
// TODO(yy): delete.
UNCOV
490
func (m *MPPayment) SetState() error {
×
UNCOV
491
        return m.setState()
×
UNCOV
492
}
×
493

494
// NeedWaitAttempts decides whether we need to hold creating more HTLC attempts
495
// and wait for the results of the payment's inflight HTLCs. Return an error if
496
// the payment is in an unexpected state.
497
func (m *MPPayment) NeedWaitAttempts() (bool, error) {
3✔
498
        // Check when the remainingAmt is not zero, which means we have more
3✔
499
        // money to be sent.
3✔
500
        if m.State.RemainingAmt != 0 {
6✔
501
                switch m.Status {
3✔
502
                // If the payment is newly created, no need to wait for HTLC
503
                // results.
UNCOV
504
                case StatusInitiated:
×
UNCOV
505
                        return false, nil
×
506

507
                // If we have inflight HTLCs, we'll check if we have terminal
508
                // states to decide if we need to wait.
UNCOV
509
                case StatusInFlight:
×
UNCOV
510
                        // We still have money to send, and one of the HTLCs is
×
UNCOV
511
                        // settled. We'd stop sending money and wait for all
×
UNCOV
512
                        // inflight HTLC attempts to finish.
×
UNCOV
513
                        if m.State.HasSettledHTLC {
×
UNCOV
514
                                log.Warnf("payment=%v has remaining amount "+
×
UNCOV
515
                                        "%v, yet at least one of its HTLCs is "+
×
UNCOV
516
                                        "settled", m.Info.PaymentIdentifier,
×
UNCOV
517
                                        m.State.RemainingAmt)
×
UNCOV
518

×
UNCOV
519
                                return true, nil
×
UNCOV
520
                        }
×
521

522
                        // The payment has a failure reason though we still
523
                        // have money to send, we'd stop sending money and wait
524
                        // for all inflight HTLC attempts to finish.
UNCOV
525
                        if m.State.PaymentFailed {
×
UNCOV
526
                                return true, nil
×
UNCOV
527
                        }
×
528

529
                        // Otherwise we don't need to wait for inflight HTLCs
530
                        // since we still have money to be sent.
UNCOV
531
                        return false, nil
×
532

533
                // We need to send more money, yet the payment is already
534
                // succeeded. Return an error in this case as the receiver is
535
                // violating the protocol.
UNCOV
536
                case StatusSucceeded:
×
UNCOV
537
                        return false, fmt.Errorf("%w: parts of the payment "+
×
UNCOV
538
                                "already succeeded but still have remaining "+
×
UNCOV
539
                                "amount %v", ErrPaymentInternal,
×
UNCOV
540
                                m.State.RemainingAmt)
×
541

542
                // The payment is failed and we have no inflight HTLCs, no need
543
                // to wait.
544
                case StatusFailed:
3✔
545
                        return false, nil
3✔
546

547
                // Unknown payment status.
UNCOV
548
                default:
×
UNCOV
549
                        return false, fmt.Errorf("%w: %s",
×
UNCOV
550
                                ErrUnknownPaymentStatus, m.Status)
×
551
                }
552
        }
553

554
        // Now we determine whether we need to wait when the remainingAmt is
555
        // already zero.
556
        switch m.Status {
3✔
557
        // When the payment is newly created, yet the payment has no remaining
558
        // amount, return an error.
UNCOV
559
        case StatusInitiated:
×
UNCOV
560
                return false, fmt.Errorf("%w: %v",
×
UNCOV
561
                        ErrPaymentInternal, m.Status)
×
562

563
        // If the payment is inflight, we must wait.
564
        //
565
        // NOTE: an edge case is when all HTLCs are failed while the payment is
566
        // not failed we'd still be in this inflight state. However, since the
567
        // remainingAmt is zero here, it means we cannot be in that state as
568
        // otherwise the remainingAmt would not be zero.
569
        case StatusInFlight:
3✔
570
                return true, nil
3✔
571

572
        // If the payment is already succeeded, no need to wait.
573
        case StatusSucceeded:
3✔
574
                return false, nil
3✔
575

576
        // If the payment is already failed, yet the remaining amount is zero,
577
        // return an error as this indicates an error state. We will only each
578
        // this status when there are no inflight HTLCs and the payment is
579
        // marked as failed with a reason, which means the remainingAmt must
580
        // not be zero because our sentAmt is zero.
UNCOV
581
        case StatusFailed:
×
UNCOV
582
                return false, fmt.Errorf("%w: %v",
×
UNCOV
583
                        ErrPaymentInternal, m.Status)
×
584

585
        // Unknown payment status.
UNCOV
586
        default:
×
UNCOV
587
                return false, fmt.Errorf("%w: %s",
×
UNCOV
588
                        ErrUnknownPaymentStatus, m.Status)
×
589
        }
590
}
591

592
// GetState returns the internal state of the payment.
593
func (m *MPPayment) GetState() *MPPaymentState {
3✔
594
        return m.State
3✔
595
}
3✔
596

597
// GetStatus returns the current status of the payment.
598
func (m *MPPayment) GetStatus() PaymentStatus {
3✔
599
        return m.Status
3✔
600
}
3✔
601

602
// GetHTLCs returns all the HTLCs for this payment.
UNCOV
603
func (m *MPPayment) GetHTLCs() []HTLCAttempt {
×
UNCOV
604
        return m.HTLCs
×
UNCOV
605
}
×
606

607
// AllowMoreAttempts is used to decide whether we can safely attempt more HTLCs
608
// for a given payment state. Return an error if the payment is in an
609
// unexpected state.
610
func (m *MPPayment) AllowMoreAttempts() (bool, error) {
3✔
611
        // Now check whether the remainingAmt is zero or not. If we don't have
3✔
612
        // any remainingAmt, no more HTLCs should be made.
3✔
613
        if m.State.RemainingAmt == 0 {
6✔
614
                // If the payment is newly created, yet we don't have any
3✔
615
                // remainingAmt, return an error.
3✔
616
                if m.Status == StatusInitiated {
3✔
UNCOV
617
                        return false, fmt.Errorf("%w: initiated payment has "+
×
UNCOV
618
                                "zero remainingAmt",
×
UNCOV
619
                                ErrPaymentInternal)
×
UNCOV
620
                }
×
621

622
                // Otherwise, exit early since all other statuses with zero
623
                // remainingAmt indicate no more HTLCs can be made.
624
                return false, nil
3✔
625
        }
626

627
        // Otherwise, the remaining amount is not zero, we now decide whether
628
        // to make more attempts based on the payment's current status.
629
        //
630
        // If at least one of the payment's attempts is settled, yet we haven't
631
        // sent all the amount, it indicates something is wrong with the peer
632
        // as the preimage is received. In this case, return an error state.
633
        if m.Status == StatusSucceeded {
3✔
UNCOV
634
                return false, fmt.Errorf("%w: payment already succeeded but "+
×
UNCOV
635
                        "still have remaining amount %v",
×
UNCOV
636
                        ErrPaymentInternal, m.State.RemainingAmt)
×
UNCOV
637
        }
×
638

639
        // Now check if we can register a new HTLC.
640
        err := m.Registrable()
3✔
641
        if err != nil {
6✔
642
                log.Warnf("Payment(%v): cannot register HTLC attempt: %v, "+
3✔
643
                        "current status: %s", m.Info.PaymentIdentifier,
3✔
644
                        err, m.Status)
3✔
645

3✔
646
                return false, nil
3✔
647
        }
3✔
648

649
        // Now we know we can register new HTLCs.
650
        return true, nil
3✔
651
}
652

653
// serializeHTLCSettleInfo serializes the details of a settled htlc.
654
func serializeHTLCSettleInfo(w io.Writer, s *HTLCSettleInfo) error {
3✔
655
        if _, err := w.Write(s.Preimage[:]); err != nil {
3✔
656
                return err
×
657
        }
×
658

659
        if err := serializeTime(w, s.SettleTime); err != nil {
3✔
660
                return err
×
661
        }
×
662

663
        return nil
3✔
664
}
665

666
// deserializeHTLCSettleInfo deserializes the details of a settled htlc.
667
func deserializeHTLCSettleInfo(r io.Reader) (*HTLCSettleInfo, error) {
3✔
668
        s := &HTLCSettleInfo{}
3✔
669
        if _, err := io.ReadFull(r, s.Preimage[:]); err != nil {
3✔
670
                return nil, err
×
671
        }
×
672

673
        var err error
3✔
674
        s.SettleTime, err = deserializeTime(r)
3✔
675
        if err != nil {
3✔
676
                return nil, err
×
677
        }
×
678

679
        return s, nil
3✔
680
}
681

682
// serializeHTLCFailInfo serializes the details of a failed htlc including the
683
// wire failure.
684
func serializeHTLCFailInfo(w io.Writer, f *HTLCFailInfo) error {
3✔
685
        if err := serializeTime(w, f.FailTime); err != nil {
3✔
686
                return err
×
687
        }
×
688

689
        // Write failure. If there is no failure message, write an empty
690
        // byte slice.
691
        var messageBytes bytes.Buffer
3✔
692
        if f.Message != nil {
6✔
693
                err := lnwire.EncodeFailureMessage(&messageBytes, f.Message, 0)
3✔
694
                if err != nil {
3✔
695
                        return err
×
696
                }
×
697
        }
698
        if err := wire.WriteVarBytes(w, 0, messageBytes.Bytes()); err != nil {
3✔
699
                return err
×
700
        }
×
701

702
        return WriteElements(w, byte(f.Reason), f.FailureSourceIndex)
3✔
703
}
704

705
// deserializeHTLCFailInfo deserializes the details of a failed htlc including
706
// the wire failure.
707
func deserializeHTLCFailInfo(r io.Reader) (*HTLCFailInfo, error) {
3✔
708
        f := &HTLCFailInfo{}
3✔
709
        var err error
3✔
710
        f.FailTime, err = deserializeTime(r)
3✔
711
        if err != nil {
3✔
712
                return nil, err
×
713
        }
×
714

715
        // Read failure.
716
        failureBytes, err := wire.ReadVarBytes(
3✔
717
                r, 0, math.MaxUint16, "failure",
3✔
718
        )
3✔
719
        if err != nil {
3✔
720
                return nil, err
×
721
        }
×
722
        if len(failureBytes) > 0 {
6✔
723
                f.Message, err = lnwire.DecodeFailureMessage(
3✔
724
                        bytes.NewReader(failureBytes), 0,
3✔
725
                )
3✔
726
                if err != nil {
3✔
727
                        return nil, err
×
728
                }
×
729
        }
730

731
        var reason byte
3✔
732
        err = ReadElements(r, &reason, &f.FailureSourceIndex)
3✔
733
        if err != nil {
3✔
734
                return nil, err
×
735
        }
×
736
        f.Reason = HTLCFailReason(reason)
3✔
737

3✔
738
        return f, nil
3✔
739
}
740

741
// generateSphinxPacket generates then encodes a sphinx packet which encodes
742
// the onion route specified by the passed layer 3 route. The blob returned
743
// from this function can immediately be included within an HTLC add packet to
744
// be sent to the first hop within the route.
745
func generateSphinxPacket(rt *route.Route, paymentHash []byte,
746
        sessionKey *btcec.PrivateKey) ([]byte, *sphinx.Circuit, error) {
3✔
747

3✔
748
        // Now that we know we have an actual route, we'll map the route into a
3✔
749
        // sphinx payment path which includes per-hop payloads for each hop
3✔
750
        // that give each node within the route the necessary information
3✔
751
        // (fees, CLTV value, etc.) to properly forward the payment.
3✔
752
        sphinxPath, err := rt.ToSphinxPath()
3✔
753
        if err != nil {
3✔
UNCOV
754
                return nil, nil, err
×
UNCOV
755
        }
×
756

757
        log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v",
3✔
758
                paymentHash, lnutils.NewLogClosure(func() string {
3✔
759
                        path := make(
×
760
                                []sphinx.OnionHop, sphinxPath.TrueRouteLength(),
×
761
                        )
×
762
                        for i := range path {
×
763
                                hopCopy := sphinxPath[i]
×
764
                                path[i] = hopCopy
×
765
                        }
×
766

767
                        return spew.Sdump(path)
×
768
                }),
769
        )
770

771
        // Next generate the onion routing packet which allows us to perform
772
        // privacy preserving source routing across the network.
773
        sphinxPacket, err := sphinx.NewOnionPacket(
3✔
774
                sphinxPath, sessionKey, paymentHash,
3✔
775
                sphinx.DeterministicPacketFiller,
3✔
776
        )
3✔
777
        if err != nil {
3✔
778
                return nil, nil, err
×
779
        }
×
780

781
        // Finally, encode Sphinx packet using its wire representation to be
782
        // included within the HTLC add packet.
783
        var onionBlob bytes.Buffer
3✔
784
        if err := sphinxPacket.Encode(&onionBlob); err != nil {
3✔
785
                return nil, nil, err
×
786
        }
×
787

788
        log.Tracef("Generated sphinx packet: %v",
3✔
789
                lnutils.NewLogClosure(func() string {
3✔
790
                        // We make a copy of the ephemeral key and unset the
×
791
                        // internal curve here in order to keep the logs from
×
792
                        // getting noisy.
×
793
                        key := *sphinxPacket.EphemeralKey
×
794
                        packetCopy := *sphinxPacket
×
795
                        packetCopy.EphemeralKey = &key
×
796

×
797
                        return spew.Sdump(packetCopy)
×
798
                }),
×
799
        )
800

801
        return onionBlob.Bytes(), &sphinx.Circuit{
3✔
802
                SessionKey:  sessionKey,
3✔
803
                PaymentPath: sphinxPath.NodeKeys(),
3✔
804
        }, nil
3✔
805
}
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