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

lightningnetwork / lnd / 15160358425

21 May 2025 10:56AM UTC coverage: 58.584% (-10.4%) from 68.996%
15160358425

Pull #9847

github

web-flow
Merge 2880b9a35 into c52a6ddeb
Pull Request #9847: Refactor Payment PR 4

634 of 942 new or added lines in 17 files covered. (67.3%)

28108 existing lines in 450 files now uncovered.

97449 of 166342 relevant lines covered (58.58%)

1.82 hits per line

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

66.29
/payments/mp_payment.go
1
package payments
2

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

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

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

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

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

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

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

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

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

60
// TerminalInfo returns any HTLC settle info recorded. If no settle info is
61
// recorded, any payment level failure will be returned. If neither a settle
62
// nor a failure is recorded, both return values will be nil.
63
func (m *MPPayment) TerminalInfo() (*HTLCAttempt,
64
        *FailureReason) {
3✔
65

3✔
66
        for _, h := range m.HTLCs {
6✔
67
                if h.Settle != nil {
6✔
68
                        return &h, nil
3✔
69
                }
3✔
70
        }
71

72
        return nil, m.FailureReason
3✔
73
}
74

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

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

90
        return sent, fees
3✔
91
}
92

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

102
                inflights = append(inflights, h)
3✔
103
        }
104

105
        return inflights
3✔
106
}
107

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

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

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

133
        // Exit early if this is not inflight.
134
        if m.Status != StatusInFlight {
6✔
135
                return nil
3✔
136
        }
3✔
137

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

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

150
        // Otherwise we can add more HTLCs.
151
        return nil
3✔
152
}
153

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

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

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

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

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

3✔
187
        return nil
3✔
188
}
189

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

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

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

×
NEW
222
                                return true, nil
×
NEW
223
                        }
×
224

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

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

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

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

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

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

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

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

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

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

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

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

306
// GetHTLCs returns all the HTLCs for this payment.
NEW
307
func (m *MPPayment) GetHTLCs() []HTLCAttempt {
×
NEW
308
        return m.HTLCs
×
NEW
309
}
×
310

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

326
                // Otherwise, exit early since all other statuses with zero
327
                // remainingAmt indicate no more HTLCs can be made.
328
                return false, nil
3✔
329
        }
330

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

343
        // Now check if we can register a new HTLC.
344
        err := m.Registrable()
3✔
345
        if err != nil {
6✔
346
                log.Warnf("Payment(%v): cannot register HTLC attempt: %v, "+
3✔
347
                        "current status: %s", m.Info.PaymentIdentifier,
3✔
348
                        err, m.Status)
3✔
349

3✔
350
                return false, nil
3✔
351
        }
3✔
352

353
        // Now we know we can register new HTLCs.
354
        return true, nil
3✔
355
}
356

357
// HTLCAttemptInfo contains static information about a specific HTLC attempt
358
// for a payment. This information is used by the router to handle any errors
359
// coming back after an attempt is made, and to query the switch about the
360
// status of the attempt.
361
type HTLCAttemptInfo struct {
362
        // AttemptID is the unique ID used for this attempt.
363
        AttemptID uint64
364

365
        // sessionKey is the raw bytes ephemeral key used for this attempt.
366
        // These bytes are lazily read off disk to save ourselves the expensive
367
        // EC operations used by btcec.PrivKeyFromBytes.
368
        sessionKey [btcec.PrivKeyBytesLen]byte
369

370
        // cachedSessionKey is our fully deserialized sesionKey. This value
371
        // may be nil if the attempt has just been read from disk and its
372
        // session key has not been used yet.
373
        cachedSessionKey *btcec.PrivateKey
374

375
        // Route is the route attempted to send the HTLC.
376
        Route route.Route
377

378
        // AttemptTime is the time at which this HTLC was attempted.
379
        AttemptTime time.Time
380

381
        // Hash is the hash used for this single HTLC attempt. For AMP payments
382
        // this will differ across attempts, for non-AMP payments each attempt
383
        // will use the same hash. This can be nil for older payment attempts,
384
        // in which the payment's PaymentHash in the PaymentCreationInfo should
385
        // be used.
386
        Hash *lntypes.Hash
387

388
        // onionBlob is the cached value for onion blob created from the sphinx
389
        // construction.
390
        onionBlob [lnwire.OnionPacketSize]byte
391

392
        // circuit is the cached value for sphinx circuit.
393
        circuit *sphinx.Circuit
394
}
395

396
// NewHtlcAttempt creates a htlc attempt.
397
func NewHtlcAttempt(attemptID uint64, sessionKey *btcec.PrivateKey,
398
        route route.Route, attemptTime time.Time,
399
        hash *lntypes.Hash) (*HTLCAttempt, error) {
3✔
400

3✔
401
        var scratch [btcec.PrivKeyBytesLen]byte
3✔
402
        copy(scratch[:], sessionKey.Serialize())
3✔
403

3✔
404
        info := HTLCAttemptInfo{
3✔
405
                AttemptID:        attemptID,
3✔
406
                sessionKey:       scratch,
3✔
407
                cachedSessionKey: sessionKey,
3✔
408
                Route:            route,
3✔
409
                AttemptTime:      attemptTime,
3✔
410
                Hash:             hash,
3✔
411
        }
3✔
412

3✔
413
        if err := info.AttachOnionBlobAndCircuit(); err != nil {
3✔
NEW
414
                return nil, err
×
NEW
415
        }
×
416

417
        return &HTLCAttempt{HTLCAttemptInfo: info}, nil
3✔
418
}
419

420
// SessionKey returns the ephemeral key used for a htlc attempt. This function
421
// performs expensive ec-ops to obtain the session key if it is not cached.
422
func (h *HTLCAttemptInfo) SessionKey() *btcec.PrivateKey {
3✔
423
        if h.cachedSessionKey == nil {
6✔
424
                h.cachedSessionKey, _ = btcec.PrivKeyFromBytes(
3✔
425
                        h.sessionKey[:],
3✔
426
                )
3✔
427
        }
3✔
428

429
        return h.cachedSessionKey
3✔
430
}
431

432
// SetSessionKey sets the session key for the HTLC attempt.
433
func (h *HTLCAttemptInfo) SetSessionKey(key [btcec.PrivKeyBytesLen]byte) {
3✔
434
        h.sessionKey = key
3✔
435
        h.cachedSessionKey = nil
3✔
436
}
3✔
437

438
// OnionBlob returns the onion blob created from the sphinx construction.
439
func (h *HTLCAttemptInfo) OnionBlob() ([lnwire.OnionPacketSize]byte, error) {
3✔
440
        var zeroBytes [lnwire.OnionPacketSize]byte
3✔
441
        if h.onionBlob == zeroBytes {
3✔
NEW
442
                if err := h.AttachOnionBlobAndCircuit(); err != nil {
×
NEW
443
                        return zeroBytes, err
×
NEW
444
                }
×
445
        }
446

447
        return h.onionBlob, nil
3✔
448
}
449

450
// Circuit returns the sphinx circuit for this attempt.
451
func (h *HTLCAttemptInfo) Circuit() (*sphinx.Circuit, error) {
3✔
452
        if h.circuit == nil {
6✔
453
                if err := h.AttachOnionBlobAndCircuit(); err != nil {
3✔
NEW
454
                        return nil, err
×
NEW
455
                }
×
456
        }
457

458
        return h.circuit, nil
3✔
459
}
460

461
// AttachOnionBlobAndCircuit creates a sphinx packet and caches the onion blob
462
// and circuit for this attempt.
463
func (h *HTLCAttemptInfo) AttachOnionBlobAndCircuit() error {
3✔
464
        onionBlob, circuit, err := generateSphinxPacket(
3✔
465
                &h.Route, h.Hash[:], h.SessionKey(),
3✔
466
        )
3✔
467
        if err != nil {
3✔
NEW
468
                return err
×
NEW
469
        }
×
470

471
        copy(h.onionBlob[:], onionBlob)
3✔
472
        h.circuit = circuit
3✔
473

3✔
474
        return nil
3✔
475
}
476

477
// HTLCAttempt contains information about a specific HTLC attempt for a given
478
// payment. It contains the HTLCAttemptInfo used to send the HTLC, as well
479
// as a timestamp and any known outcome of the attempt.
480
type HTLCAttempt struct {
481
        HTLCAttemptInfo
482

483
        // Settle is the preimage of a successful payment. This serves as a
484
        // proof of payment. It will only be non-nil for settled payments.
485
        //
486
        // NOTE: Can be nil if payment is not settled.
487
        Settle *HTLCSettleInfo
488

489
        // Fail is a failure reason code indicating the reason the payment
490
        // failed. It is only non-nil for failed payments.
491
        //
492
        // NOTE: Can be nil if payment is not failed.
493
        Failure *HTLCFailInfo
494
}
495

496
// HTLCSettleInfo encapsulates the information that augments an HTLCAttempt in
497
// the event that the HTLC is successful.
498
type HTLCSettleInfo struct {
499
        // Preimage is the preimage of a successful HTLC. This serves as a proof
500
        // of payment.
501
        Preimage lntypes.Preimage
502

503
        // SettleTime is the time at which this HTLC was settled.
504
        SettleTime time.Time
505
}
506

507
// HTLCFailReason is the reason an htlc failed.
508
type HTLCFailReason byte
509

510
const (
511
        // HTLCFailUnknown is recorded for htlcs that failed with an unknown
512
        // reason.
513
        HTLCFailUnknown HTLCFailReason = 0
514

515
        // HTLCFailUnreadable is recorded for htlcs that had a failure message
516
        // that couldn't be decrypted.
517
        HTLCFailUnreadable HTLCFailReason = 1
518

519
        // HTLCFailInternal is recorded for htlcs that failed because of an
520
        // internal error.
521
        HTLCFailInternal HTLCFailReason = 2
522

523
        // HTLCFailMessage is recorded for htlcs that failed with a network
524
        // failure message.
525
        HTLCFailMessage HTLCFailReason = 3
526
)
527

528
// HTLCFailInfo encapsulates the information that augments an HTLCAttempt in the
529
// event that the HTLC fails.
530
type HTLCFailInfo struct {
531
        // FailTime is the time at which this HTLC was failed.
532
        FailTime time.Time
533

534
        // Message is the wire message that failed this HTLC. This field will be
535
        // populated when the failure reason is HTLCFailMessage.
536
        Message lnwire.FailureMessage
537

538
        // Reason is the failure reason for this HTLC.
539
        Reason HTLCFailReason
540

541
        // The position in the path of the intermediate or final node that
542
        // generated the failure message. Position zero is the sender node. This
543
        // field will be populated when the failure reason is either
544
        // HTLCFailMessage or HTLCFailUnknown.
545
        FailureSourceIndex uint32
546
}
547

548
// MPPaymentState wraps a series of info needed for a given payment, which is
549
// used by both MPP and AMP. This is a memory representation of the payment's
550
// current state and is updated whenever the payment is read from disk.
551
type MPPaymentState struct {
552
        // NumAttemptsInFlight specifies the number of HTLCs the payment is
553
        // waiting results for.
554
        NumAttemptsInFlight int
555

556
        // RemainingAmt specifies how much more money to be sent.
557
        RemainingAmt lnwire.MilliSatoshi
558

559
        // FeesPaid specifies the total fees paid so far that can be used to
560
        // calculate remaining fee budget.
561
        FeesPaid lnwire.MilliSatoshi
562

563
        // HasSettledHTLC is true if at least one of the payment's HTLCs is
564
        // settled.
565
        HasSettledHTLC bool
566

567
        // PaymentFailed is true if the payment has been marked as failed with
568
        // a reason.
569
        PaymentFailed bool
570
}
571

572
// generateSphinxPacket generates then encodes a sphinx packet which encodes
573
// the onion route specified by the passed layer 3 route. The blob returned
574
// from this function can immediately be included within an HTLC add packet to
575
// be sent to the first hop within the route.
576
func generateSphinxPacket(rt *route.Route, paymentHash []byte,
577
        sessionKey *btcec.PrivateKey) ([]byte, *sphinx.Circuit, error) {
3✔
578

3✔
579
        // Now that we know we have an actual route, we'll map the route into a
3✔
580
        // sphinx payment path which includes per-hop payloads for each hop
3✔
581
        // that give each node within the route the necessary information
3✔
582
        // (fees, CLTV value, etc.) to properly forward the payment.
3✔
583
        sphinxPath, err := rt.ToSphinxPath()
3✔
584
        if err != nil {
3✔
NEW
585
                return nil, nil, err
×
NEW
586
        }
×
587

588
        log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v",
3✔
589
                paymentHash, lnutils.NewLogClosure(func() string {
3✔
NEW
590
                        path := make(
×
NEW
591
                                []sphinx.OnionHop, sphinxPath.TrueRouteLength(),
×
NEW
592
                        )
×
NEW
593
                        for i := range path {
×
NEW
594
                                hopCopy := sphinxPath[i]
×
NEW
595
                                path[i] = hopCopy
×
NEW
596
                        }
×
597

NEW
598
                        return spew.Sdump(path)
×
599
                }),
600
        )
601

602
        // Next generate the onion routing packet which allows us to perform
603
        // privacy preserving source routing across the network.
604
        sphinxPacket, err := sphinx.NewOnionPacket(
3✔
605
                sphinxPath, sessionKey, paymentHash,
3✔
606
                sphinx.DeterministicPacketFiller,
3✔
607
        )
3✔
608
        if err != nil {
3✔
NEW
609
                return nil, nil, err
×
NEW
610
        }
×
611

612
        // Finally, encode Sphinx packet using its wire representation to be
613
        // included within the HTLC add packet.
614
        var onionBlob bytes.Buffer
3✔
615
        if err := sphinxPacket.Encode(&onionBlob); err != nil {
3✔
NEW
616
                return nil, nil, err
×
NEW
617
        }
×
618

619
        log.Tracef("Generated sphinx packet: %v",
3✔
620
                lnutils.NewLogClosure(func() string {
3✔
NEW
621
                        // We make a copy of the ephemeral key and unset the
×
NEW
622
                        // internal curve here in order to keep the logs from
×
NEW
623
                        // getting noisy.
×
NEW
624
                        key := *sphinxPacket.EphemeralKey
×
NEW
625
                        packetCopy := *sphinxPacket
×
NEW
626
                        packetCopy.EphemeralKey = &key
×
NEW
627

×
NEW
628
                        return spew.Sdump(packetCopy)
×
NEW
629
                }),
×
630
        )
631

632
        return onionBlob.Bytes(), &sphinx.Circuit{
3✔
633
                SessionKey:  sessionKey,
3✔
634
                PaymentPath: sphinxPath.NodeKeys(),
3✔
635
        }, nil
3✔
636
}
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