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

lightningnetwork / lnd / 16915531902

12 Aug 2025 04:59PM UTC coverage: 66.948%. First build
16915531902

Pull #10151

github

web-flow
Merge d106ffd13 into 4e0af2f49
Pull Request #10151: Refactor Payment PR 5

246 of 321 new or added lines in 20 files covered. (76.64%)

135783 of 202820 relevant lines covered (66.95%)

21592.59 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

NEW
73
        return "unknown"
×
74
}
75

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

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

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

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

92
        // FirstHopCustomRecords are the TLV records that are to be sent to the
93
        // first hop of this payment. These records will be transmitted via the
94
        // wire message only and therefore do not affect the 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) {
206✔
147

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

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

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

164
        return &HTLCAttempt{HTLCAttemptInfo: info}, nil
205✔
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 {
218✔
170
        if h.cachedSessionKey == nil {
232✔
171
                h.cachedSessionKey, _ = btcec.PrivKeyFromBytes(
14✔
172
                        h.sessionKey[:],
14✔
173
                )
14✔
174
        }
14✔
175

176
        return h.cachedSessionKey
218✔
177
}
178

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

188
        return h.onionBlob, nil
37✔
189
}
190

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

199
        return h.circuit, nil
38✔
200
}
201

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

212
        copy(h.onionBlob[:], onionBlob)
215✔
213
        h.circuit = circuit
215✔
214

215✔
215
        return nil
215✔
216
}
217

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

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

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

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

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

248
// HTLCFailReason is the reason an htlc failed.
249
type HTLCFailReason byte
250

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

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

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

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

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

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

279
        // Reason is the failure reason for this HTLC.
280
        Reason HTLCFailReason
281

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

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

297
        // RemainingAmt specifies how much more money to be sent.
298
        RemainingAmt lnwire.MilliSatoshi
299

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

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

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

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

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

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

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

339
        // Status is the current PaymentStatus of this payment.
340
        Status PaymentStatus
341

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

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

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

365
        return nil, m.FailureReason
634✔
366
}
367

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

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

383
        return sent, fees
783✔
384
}
385

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

395
                inflights = append(inflights, h)
376✔
396
        }
397

398
        return inflights
802✔
399
}
400

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

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

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

426
        // Exit early if this is not inflight.
427
        if m.Status != StatusInFlight {
163✔
428
                return nil
49✔
429
        }
49✔
430

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

438
        // If the payment is already failed, we won't allow adding more HTLCs.
439
        if m.State.PaymentFailed {
67✔
440
                return ErrPaymentPendingFailed
6✔
441
        }
6✔
442

443
        // Otherwise we can add more HTLCs.
444
        return nil
55✔
445
}
446

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

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

461
        // Get any terminal info for this payment.
462
        settle, failure := m.TerminalInfo()
720✔
463

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

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

720✔
480
        return nil
720✔
481
}
482

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

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

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

1✔
517
                                return true, nil
1✔
518
                        }
1✔
519

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

527
                        // Otherwise we don't need to wait for inflight HTLCs
528
                        // since we still have money to be sent.
529
                        return false, nil
1✔
530

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

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

545
                // Unknown payment status.
546
                default:
1✔
547
                        return false, fmt.Errorf("%w: %s",
1✔
548
                                ErrUnknownPaymentStatus, m.Status)
1✔
549
                }
550
        }
551

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

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

570
        // If the payment is already succeeded, no need to wait.
571
        case StatusSucceeded:
14✔
572
                return false, nil
14✔
573

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

583
        // Unknown payment status.
584
        default:
1✔
585
                return false, fmt.Errorf("%w: %s",
1✔
586
                        ErrUnknownPaymentStatus, m.Status)
1✔
587
        }
588
}
589

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

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

600
// GetHTLCs returns all the HTLCs for this payment.
601
func (m *MPPayment) GetHTLCs() []HTLCAttempt {
1✔
602
        return m.HTLCs
1✔
603
}
1✔
604

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

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

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

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

10✔
644
                return false, nil
10✔
645
        }
10✔
646

647
        // Now we know we can register new HTLCs.
648
        return true, nil
29✔
649
}
650

651
// deserializeTime deserializes time as unix nanoseconds.
652
func deserializeTime(r io.Reader) (time.Time, error) {
1,742✔
653
        var scratch [8]byte
1,742✔
654
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
1,742✔
655
                return time.Time{}, err
×
656
        }
×
657

658
        // Convert to time.Time. Interpret unix nano time zero as a zero
659
        // time.Time value.
660
        unixNano := byteOrder.Uint64(scratch[:])
1,742✔
661
        if unixNano == 0 {
2,837✔
662
                return time.Time{}, nil
1,095✔
663
        }
1,095✔
664

665
        return time.Unix(0, int64(unixNano)), nil
647✔
666
}
667

668
// serializeTime serializes time as unix nanoseconds.
669
func serializeTime(w io.Writer, t time.Time) error {
294✔
670
        var scratch [8]byte
294✔
671

294✔
672
        // Convert to unix nano seconds, but only if time is non-zero. Calling
294✔
673
        // UnixNano() on a zero time yields an undefined result.
294✔
674
        var unixNano int64
294✔
675
        if !t.IsZero() {
471✔
676
                unixNano = t.UnixNano()
177✔
677
        }
177✔
678

679
        byteOrder.PutUint64(scratch[:], uint64(unixNano))
294✔
680
        _, err := w.Write(scratch[:])
294✔
681
        return err
294✔
682
}
683

684
// generateSphinxPacket generates then encodes a sphinx packet which encodes
685
// the onion route specified by the passed layer 3 route. The blob returned
686
// from this function can immediately be included within an HTLC add packet to
687
// be sent to the first hop within the route.
688
func generateSphinxPacket(rt *route.Route, paymentHash []byte,
689
        sessionKey *btcec.PrivateKey) ([]byte, *sphinx.Circuit, error) {
217✔
690

217✔
691
        // Now that we know we have an actual route, we'll map the route into a
217✔
692
        // sphinx payment path which includes per-hop payloads for each hop
217✔
693
        // that give each node within the route the necessary information
217✔
694
        // (fees, CLTV value, etc.) to properly forward the payment.
217✔
695
        sphinxPath, err := rt.ToSphinxPath()
217✔
696
        if err != nil {
219✔
697
                return nil, nil, err
2✔
698
        }
2✔
699

700
        log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v",
215✔
701
                paymentHash, lnutils.NewLogClosure(func() string {
215✔
702
                        path := make(
×
703
                                []sphinx.OnionHop, sphinxPath.TrueRouteLength(),
×
704
                        )
×
705
                        for i := range path {
×
706
                                hopCopy := sphinxPath[i]
×
707
                                path[i] = hopCopy
×
708
                        }
×
709

710
                        return spew.Sdump(path)
×
711
                }),
712
        )
713

714
        // Next generate the onion routing packet which allows us to perform
715
        // privacy preserving source routing across the network.
716
        sphinxPacket, err := sphinx.NewOnionPacket(
215✔
717
                sphinxPath, sessionKey, paymentHash,
215✔
718
                sphinx.DeterministicPacketFiller,
215✔
719
        )
215✔
720
        if err != nil {
215✔
721
                return nil, nil, err
×
722
        }
×
723

724
        // Finally, encode Sphinx packet using its wire representation to be
725
        // included within the HTLC add packet.
726
        var onionBlob bytes.Buffer
215✔
727
        if err := sphinxPacket.Encode(&onionBlob); err != nil {
215✔
728
                return nil, nil, err
×
729
        }
×
730

731
        log.Tracef("Generated sphinx packet: %v",
215✔
732
                lnutils.NewLogClosure(func() string {
215✔
733
                        // We make a copy of the ephemeral key and unset the
×
734
                        // internal curve here in order to keep the logs from
×
735
                        // getting noisy.
×
736
                        key := *sphinxPacket.EphemeralKey
×
737
                        packetCopy := *sphinxPacket
×
738
                        packetCopy.EphemeralKey = &key
×
739

×
740
                        return spew.Sdump(packetCopy)
×
741
                }),
×
742
        )
743

744
        return onionBlob.Bytes(), &sphinx.Circuit{
215✔
745
                SessionKey:  sessionKey,
215✔
746
                PaymentPath: sphinxPath.NodeKeys(),
215✔
747
        }, nil
215✔
748
}
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