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

lightningnetwork / lnd / 16948521526

13 Aug 2025 08:27PM UTC coverage: 54.877% (-12.1%) from 66.929%
16948521526

Pull #10155

github

web-flow
Merge 61c0fecf6 into c6a9116e3
Pull Request #10155: Add missing invoice index for native sql

108941 of 198518 relevant lines covered (54.88%)

22023.66 hits per line

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

82.7
/channeldb/mp_payment.go
1
package channeldb
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
        paymentsdb "github.com/lightningnetwork/lnd/payments/db"
19
        "github.com/lightningnetwork/lnd/routing/route"
20
)
21

22
// HTLCAttemptInfo contains static information about a specific HTLC attempt
23
// for a payment. This information is used by the router to handle any errors
24
// coming back after an attempt is made, and to query the switch about the
25
// status of the attempt.
26
type HTLCAttemptInfo struct {
27
        // AttemptID is the unique ID used for this attempt.
28
        AttemptID uint64
29

30
        // sessionKey is the raw bytes ephemeral key used for this attempt.
31
        // These bytes are lazily read off disk to save ourselves the expensive
32
        // EC operations used by btcec.PrivKeyFromBytes.
33
        sessionKey [btcec.PrivKeyBytesLen]byte
34

35
        // cachedSessionKey is our fully deserialized sesionKey. This value
36
        // may be nil if the attempt has just been read from disk and its
37
        // session key has not been used yet.
38
        cachedSessionKey *btcec.PrivateKey
39

40
        // Route is the route attempted to send the HTLC.
41
        Route route.Route
42

43
        // AttemptTime is the time at which this HTLC was attempted.
44
        AttemptTime time.Time
45

46
        // Hash is the hash used for this single HTLC attempt. For AMP payments
47
        // this will differ across attempts, for non-AMP payments each attempt
48
        // will use the same hash. This can be nil for older payment attempts,
49
        // in which the payment's PaymentHash in the PaymentCreationInfo should
50
        // be used.
51
        Hash *lntypes.Hash
52

53
        // onionBlob is the cached value for onion blob created from the sphinx
54
        // construction.
55
        onionBlob [lnwire.OnionPacketSize]byte
56

57
        // circuit is the cached value for sphinx circuit.
58
        circuit *sphinx.Circuit
59
}
60

61
// NewHtlcAttempt creates a htlc attempt.
62
func NewHtlcAttempt(attemptID uint64, sessionKey *btcec.PrivateKey,
63
        route route.Route, attemptTime time.Time,
64
        hash *lntypes.Hash) (*HTLCAttempt, error) {
203✔
65

203✔
66
        var scratch [btcec.PrivKeyBytesLen]byte
203✔
67
        copy(scratch[:], sessionKey.Serialize())
203✔
68

203✔
69
        info := HTLCAttemptInfo{
203✔
70
                AttemptID:        attemptID,
203✔
71
                sessionKey:       scratch,
203✔
72
                cachedSessionKey: sessionKey,
203✔
73
                Route:            route,
203✔
74
                AttemptTime:      attemptTime,
203✔
75
                Hash:             hash,
203✔
76
        }
203✔
77

203✔
78
        if err := info.attachOnionBlobAndCircuit(); err != nil {
204✔
79
                return nil, err
1✔
80
        }
1✔
81

82
        return &HTLCAttempt{HTLCAttemptInfo: info}, nil
202✔
83
}
84

85
// SessionKey returns the ephemeral key used for a htlc attempt. This function
86
// performs expensive ec-ops to obtain the session key if it is not cached.
87
func (h *HTLCAttemptInfo) SessionKey() *btcec.PrivateKey {
215✔
88
        if h.cachedSessionKey == nil {
226✔
89
                h.cachedSessionKey, _ = btcec.PrivKeyFromBytes(
11✔
90
                        h.sessionKey[:],
11✔
91
                )
11✔
92
        }
11✔
93

94
        return h.cachedSessionKey
215✔
95
}
96

97
// OnionBlob returns the onion blob created from the sphinx construction.
98
func (h *HTLCAttemptInfo) OnionBlob() ([lnwire.OnionPacketSize]byte, error) {
34✔
99
        var zeroBytes [lnwire.OnionPacketSize]byte
34✔
100
        if h.onionBlob == zeroBytes {
34✔
101
                if err := h.attachOnionBlobAndCircuit(); err != nil {
×
102
                        return zeroBytes, err
×
103
                }
×
104
        }
105

106
        return h.onionBlob, nil
34✔
107
}
108

109
// Circuit returns the sphinx circuit for this attempt.
110
func (h *HTLCAttemptInfo) Circuit() (*sphinx.Circuit, error) {
35✔
111
        if h.circuit == nil {
44✔
112
                if err := h.attachOnionBlobAndCircuit(); err != nil {
9✔
113
                        return nil, err
×
114
                }
×
115
        }
116

117
        return h.circuit, nil
35✔
118
}
119

120
// attachOnionBlobAndCircuit creates a sphinx packet and caches the onion blob
121
// and circuit for this attempt.
122
func (h *HTLCAttemptInfo) attachOnionBlobAndCircuit() error {
213✔
123
        onionBlob, circuit, err := generateSphinxPacket(
213✔
124
                &h.Route, h.Hash[:], h.SessionKey(),
213✔
125
        )
213✔
126
        if err != nil {
214✔
127
                return err
1✔
128
        }
1✔
129

130
        copy(h.onionBlob[:], onionBlob)
212✔
131
        h.circuit = circuit
212✔
132

212✔
133
        return nil
212✔
134
}
135

136
// HTLCAttempt contains information about a specific HTLC attempt for a given
137
// payment. It contains the HTLCAttemptInfo used to send the HTLC, as well
138
// as a timestamp and any known outcome of the attempt.
139
type HTLCAttempt struct {
140
        HTLCAttemptInfo
141

142
        // Settle is the preimage of a successful payment. This serves as a
143
        // proof of payment. It will only be non-nil for settled payments.
144
        //
145
        // NOTE: Can be nil if payment is not settled.
146
        Settle *HTLCSettleInfo
147

148
        // Fail is a failure reason code indicating the reason the payment
149
        // failed. It is only non-nil for failed payments.
150
        //
151
        // NOTE: Can be nil if payment is not failed.
152
        Failure *HTLCFailInfo
153
}
154

155
// HTLCSettleInfo encapsulates the information that augments an HTLCAttempt in
156
// the event that the HTLC is successful.
157
type HTLCSettleInfo struct {
158
        // Preimage is the preimage of a successful HTLC. This serves as a proof
159
        // of payment.
160
        Preimage lntypes.Preimage
161

162
        // SettleTime is the time at which this HTLC was settled.
163
        SettleTime time.Time
164
}
165

166
// HTLCFailReason is the reason an htlc failed.
167
type HTLCFailReason byte
168

169
const (
170
        // HTLCFailUnknown is recorded for htlcs that failed with an unknown
171
        // reason.
172
        HTLCFailUnknown HTLCFailReason = 0
173

174
        // HTLCFailUnknown is recorded for htlcs that had a failure message that
175
        // couldn't be decrypted.
176
        HTLCFailUnreadable HTLCFailReason = 1
177

178
        // HTLCFailInternal is recorded for htlcs that failed because of an
179
        // internal error.
180
        HTLCFailInternal HTLCFailReason = 2
181

182
        // HTLCFailMessage is recorded for htlcs that failed with a network
183
        // failure message.
184
        HTLCFailMessage HTLCFailReason = 3
185
)
186

187
// HTLCFailInfo encapsulates the information that augments an HTLCAttempt in the
188
// event that the HTLC fails.
189
type HTLCFailInfo struct {
190
        // FailTime is the time at which this HTLC was failed.
191
        FailTime time.Time
192

193
        // Message is the wire message that failed this HTLC. This field will be
194
        // populated when the failure reason is HTLCFailMessage.
195
        Message lnwire.FailureMessage
196

197
        // Reason is the failure reason for this HTLC.
198
        Reason HTLCFailReason
199

200
        // The position in the path of the intermediate or final node that
201
        // generated the failure message. Position zero is the sender node. This
202
        // field will be populated when the failure reason is either
203
        // HTLCFailMessage or HTLCFailUnknown.
204
        FailureSourceIndex uint32
205
}
206

207
// MPPaymentState wraps a series of info needed for a given payment, which is
208
// used by both MPP and AMP. This is a memory representation of the payment's
209
// current state and is updated whenever the payment is read from disk.
210
type MPPaymentState struct {
211
        // NumAttemptsInFlight specifies the number of HTLCs the payment is
212
        // waiting results for.
213
        NumAttemptsInFlight int
214

215
        // RemainingAmt specifies how much more money to be sent.
216
        RemainingAmt lnwire.MilliSatoshi
217

218
        // FeesPaid specifies the total fees paid so far that can be used to
219
        // calculate remaining fee budget.
220
        FeesPaid lnwire.MilliSatoshi
221

222
        // HasSettledHTLC is true if at least one of the payment's HTLCs is
223
        // settled.
224
        HasSettledHTLC bool
225

226
        // PaymentFailed is true if the payment has been marked as failed with
227
        // a reason.
228
        PaymentFailed bool
229
}
230

231
// MPPayment is a wrapper around a payment's PaymentCreationInfo and
232
// HTLCAttempts. All payments will have the PaymentCreationInfo set, any
233
// HTLCs made in attempts to be completed will populated in the HTLCs slice.
234
// Each populated HTLCAttempt represents an attempted HTLC, each of which may
235
// have the associated Settle or Fail struct populated if the HTLC is no longer
236
// in-flight.
237
type MPPayment struct {
238
        // SequenceNum is a unique identifier used to sort the payments in
239
        // order of creation.
240
        SequenceNum uint64
241

242
        // Info holds all static information about this payment, and is
243
        // populated when the payment is initiated.
244
        Info *PaymentCreationInfo
245

246
        // HTLCs holds the information about individual HTLCs that we send in
247
        // order to make the payment.
248
        HTLCs []HTLCAttempt
249

250
        // FailureReason is the failure reason code indicating the reason the
251
        // payment failed.
252
        //
253
        // NOTE: Will only be set once the daemon has given up on the payment
254
        // altogether.
255
        FailureReason *FailureReason
256

257
        // Status is the current PaymentStatus of this payment.
258
        Status PaymentStatus
259

260
        // State is the current state of the payment that holds a number of key
261
        // insights and is used to determine what to do on each payment loop
262
        // iteration.
263
        State *MPPaymentState
264
}
265

266
// Terminated returns a bool to specify whether the payment is in a terminal
267
// state.
268
func (m *MPPayment) Terminated() bool {
53✔
269
        // If the payment is in terminal state, it cannot be updated.
53✔
270
        return m.Status.updatable() != nil
53✔
271
}
53✔
272

273
// TerminalInfo returns any HTLC settle info recorded. If no settle info is
274
// recorded, any payment level failure will be returned. If neither a settle
275
// nor a failure is recorded, both return values will be nil.
276
func (m *MPPayment) TerminalInfo() (*HTLCAttempt, *FailureReason) {
722✔
277
        for _, h := range m.HTLCs {
1,439✔
278
                if h.Settle != nil {
819✔
279
                        return &h, nil
102✔
280
                }
102✔
281
        }
282

283
        return nil, m.FailureReason
620✔
284
}
285

286
// SentAmt returns the sum of sent amount and fees for HTLCs that are either
287
// settled or still in flight.
288
func (m *MPPayment) SentAmt() (lnwire.MilliSatoshi, lnwire.MilliSatoshi) {
760✔
289
        var sent, fees lnwire.MilliSatoshi
760✔
290
        for _, h := range m.HTLCs {
1,525✔
291
                if h.Failure != nil {
1,105✔
292
                        continue
340✔
293
                }
294

295
                // The attempt was not failed, meaning the amount was
296
                // potentially sent to the receiver.
297
                sent += h.Route.ReceiverAmt()
425✔
298
                fees += h.Route.TotalFees()
425✔
299
        }
300

301
        return sent, fees
760✔
302
}
303

304
// InFlightHTLCs returns the HTLCs that are still in-flight, meaning they have
305
// not been settled or failed.
306
func (m *MPPayment) InFlightHTLCs() []HTLCAttempt {
775✔
307
        var inflights []HTLCAttempt
775✔
308
        for _, h := range m.HTLCs {
1,543✔
309
                if h.Settle != nil || h.Failure != nil {
1,199✔
310
                        continue
431✔
311
                }
312

313
                inflights = append(inflights, h)
337✔
314
        }
315

316
        return inflights
775✔
317
}
318

319
// GetAttempt returns the specified htlc attempt on the payment.
320
func (m *MPPayment) GetAttempt(id uint64) (*HTLCAttempt, error) {
6✔
321
        // TODO(yy): iteration can be slow, make it into a tree or use BS.
6✔
322
        for _, htlc := range m.HTLCs {
12✔
323
                htlc := htlc
6✔
324
                if htlc.AttemptID == id {
12✔
325
                        return &htlc, nil
6✔
326
                }
6✔
327
        }
328

329
        return nil, errors.New("htlc attempt not found on payment")
×
330
}
331

332
// Registrable returns an error to specify whether adding more HTLCs to the
333
// payment with its current status is allowed. A payment can accept new HTLC
334
// registrations when it's newly created, or none of its HTLCs is in a terminal
335
// state.
336
func (m *MPPayment) Registrable() error {
111✔
337
        // If updating the payment is not allowed, we can't register new HTLCs.
111✔
338
        // Otherwise, the status must be either `StatusInitiated` or
111✔
339
        // `StatusInFlight`.
111✔
340
        if err := m.Status.updatable(); err != nil {
123✔
341
                return err
12✔
342
        }
12✔
343

344
        // Exit early if this is not inflight.
345
        if m.Status != StatusInFlight {
145✔
346
                return nil
46✔
347
        }
46✔
348

349
        // There are still inflight HTLCs and we need to check whether there
350
        // are settled HTLCs or the payment is failed. If we already have
351
        // settled HTLCs, we won't allow adding more HTLCs.
352
        if m.State.HasSettledHTLC {
58✔
353
                return paymentsdb.ErrPaymentPendingSettled
5✔
354
        }
5✔
355

356
        // If the payment is already failed, we won't allow adding more HTLCs.
357
        if m.State.PaymentFailed {
52✔
358
                return paymentsdb.ErrPaymentPendingFailed
4✔
359
        }
4✔
360

361
        // Otherwise we can add more HTLCs.
362
        return nil
44✔
363
}
364

365
// setState creates and attaches a new MPPaymentState to the payment. It also
366
// updates the payment's status based on its current state.
367
func (m *MPPayment) setState() error {
702✔
368
        // Fetch the total amount and fees that has already been sent in
702✔
369
        // settled and still in-flight shards.
702✔
370
        sentAmt, fees := m.SentAmt()
702✔
371

702✔
372
        // Sanity check we haven't sent a value larger than the payment amount.
702✔
373
        totalAmt := m.Info.Value
702✔
374
        if sentAmt > totalAmt {
703✔
375
                return fmt.Errorf("%w: sent=%v, total=%v",
1✔
376
                        paymentsdb.ErrSentExceedsTotal, sentAmt, totalAmt)
1✔
377
        }
1✔
378

379
        // Get any terminal info for this payment.
380
        settle, failure := m.TerminalInfo()
701✔
381

701✔
382
        // Now determine the payment's status.
701✔
383
        status, err := decidePaymentStatus(m.HTLCs, m.FailureReason)
701✔
384
        if err != nil {
701✔
385
                return err
×
386
        }
×
387

388
        // Update the payment state and status.
389
        m.State = &MPPaymentState{
701✔
390
                NumAttemptsInFlight: len(m.InFlightHTLCs()),
701✔
391
                RemainingAmt:        totalAmt - sentAmt,
701✔
392
                FeesPaid:            fees,
701✔
393
                HasSettledHTLC:      settle != nil,
701✔
394
                PaymentFailed:       failure != nil,
701✔
395
        }
701✔
396
        m.Status = status
701✔
397

701✔
398
        return nil
701✔
399
}
400

401
// SetState calls the internal method setState. This is a temporary method
402
// to be used by the tests in routing. Once the tests are updated to use mocks,
403
// this method can be removed.
404
//
405
// TODO(yy): delete.
406
func (m *MPPayment) SetState() error {
74✔
407
        return m.setState()
74✔
408
}
74✔
409

410
// NeedWaitAttempts decides whether we need to hold creating more HTLC attempts
411
// and wait for the results of the payment's inflight HTLCs. Return an error if
412
// the payment is in an unexpected state.
413
func (m *MPPayment) NeedWaitAttempts() (bool, error) {
45✔
414
        // Check when the remainingAmt is not zero, which means we have more
45✔
415
        // money to be sent.
45✔
416
        if m.State.RemainingAmt != 0 {
54✔
417
                switch m.Status {
9✔
418
                // If the payment is newly created, no need to wait for HTLC
419
                // results.
420
                case StatusInitiated:
1✔
421
                        return false, nil
1✔
422

423
                // If we have inflight HTLCs, we'll check if we have terminal
424
                // states to decide if we need to wait.
425
                case StatusInFlight:
3✔
426
                        // We still have money to send, and one of the HTLCs is
3✔
427
                        // settled. We'd stop sending money and wait for all
3✔
428
                        // inflight HTLC attempts to finish.
3✔
429
                        if m.State.HasSettledHTLC {
4✔
430
                                log.Warnf("payment=%v has remaining amount "+
1✔
431
                                        "%v, yet at least one of its HTLCs is "+
1✔
432
                                        "settled", m.Info.PaymentIdentifier,
1✔
433
                                        m.State.RemainingAmt)
1✔
434

1✔
435
                                return true, nil
1✔
436
                        }
1✔
437

438
                        // The payment has a failure reason though we still
439
                        // have money to send, we'd stop sending money and wait
440
                        // for all inflight HTLC attempts to finish.
441
                        if m.State.PaymentFailed {
3✔
442
                                return true, nil
1✔
443
                        }
1✔
444

445
                        // Otherwise we don't need to wait for inflight HTLCs
446
                        // since we still have money to be sent.
447
                        return false, nil
1✔
448

449
                // We need to send more money, yet the payment is already
450
                // succeeded. Return an error in this case as the receiver is
451
                // violating the protocol.
452
                case StatusSucceeded:
1✔
453
                        return false, fmt.Errorf("%w: parts of the payment "+
1✔
454
                                "already succeeded but still have remaining "+
1✔
455
                                "amount %v", paymentsdb.ErrPaymentInternal,
1✔
456
                                m.State.RemainingAmt)
1✔
457

458
                // The payment is failed and we have no inflight HTLCs, no need
459
                // to wait.
460
                case StatusFailed:
3✔
461
                        return false, nil
3✔
462

463
                // Unknown payment status.
464
                default:
1✔
465
                        return false, fmt.Errorf("%w: %s",
1✔
466
                                paymentsdb.ErrUnknownPaymentStatus, m.Status)
1✔
467
                }
468
        }
469

470
        // Now we determine whether we need to wait when the remainingAmt is
471
        // already zero.
472
        switch m.Status {
36✔
473
        // When the payment is newly created, yet the payment has no remaining
474
        // amount, return an error.
475
        case StatusInitiated:
1✔
476
                return false, fmt.Errorf("%w: %v",
1✔
477
                        paymentsdb.ErrPaymentInternal, m.Status)
1✔
478

479
        // If the payment is inflight, we must wait.
480
        //
481
        // NOTE: an edge case is when all HTLCs are failed while the payment is
482
        // not failed we'd still be in this inflight state. However, since the
483
        // remainingAmt is zero here, it means we cannot be in that state as
484
        // otherwise the remainingAmt would not be zero.
485
        case StatusInFlight:
22✔
486
                return true, nil
22✔
487

488
        // If the payment is already succeeded, no need to wait.
489
        case StatusSucceeded:
11✔
490
                return false, nil
11✔
491

492
        // If the payment is already failed, yet the remaining amount is zero,
493
        // return an error as this indicates an error state. We will only each
494
        // this status when there are no inflight HTLCs and the payment is
495
        // marked as failed with a reason, which means the remainingAmt must
496
        // not be zero because our sentAmt is zero.
497
        case StatusFailed:
1✔
498
                return false, fmt.Errorf("%w: %v",
1✔
499
                        paymentsdb.ErrPaymentInternal, m.Status)
1✔
500

501
        // Unknown payment status.
502
        default:
1✔
503
                return false, fmt.Errorf("%w: %s",
1✔
504
                        paymentsdb.ErrUnknownPaymentStatus, m.Status)
1✔
505
        }
506
}
507

508
// GetState returns the internal state of the payment.
509
func (m *MPPayment) GetState() *MPPaymentState {
57✔
510
        return m.State
57✔
511
}
57✔
512

513
// Status returns the current status of the payment.
514
func (m *MPPayment) GetStatus() PaymentStatus {
158✔
515
        return m.Status
158✔
516
}
158✔
517

518
// GetPayment returns all the HTLCs for this payment.
519
func (m *MPPayment) GetHTLCs() []HTLCAttempt {
1✔
520
        return m.HTLCs
1✔
521
}
1✔
522

523
// AllowMoreAttempts is used to decide whether we can safely attempt more HTLCs
524
// for a given payment state. Return an error if the payment is in an
525
// unexpected state.
526
func (m *MPPayment) AllowMoreAttempts() (bool, error) {
72✔
527
        // Now check whether the remainingAmt is zero or not. If we don't have
72✔
528
        // any remainingAmt, no more HTLCs should be made.
72✔
529
        if m.State.RemainingAmt == 0 {
110✔
530
                // If the payment is newly created, yet we don't have any
38✔
531
                // remainingAmt, return an error.
38✔
532
                if m.Status == StatusInitiated {
39✔
533
                        return false, fmt.Errorf("%w: initiated payment has "+
1✔
534
                                "zero remainingAmt",
1✔
535
                                paymentsdb.ErrPaymentInternal)
1✔
536
                }
1✔
537

538
                // Otherwise, exit early since all other statuses with zero
539
                // remainingAmt indicate no more HTLCs can be made.
540
                return false, nil
37✔
541
        }
542

543
        // Otherwise, the remaining amount is not zero, we now decide whether
544
        // to make more attempts based on the payment's current status.
545
        //
546
        // If at least one of the payment's attempts is settled, yet we haven't
547
        // sent all the amount, it indicates something is wrong with the peer
548
        // as the preimage is received. In this case, return an error state.
549
        if m.Status == StatusSucceeded {
35✔
550
                return false, fmt.Errorf("%w: payment already succeeded but "+
1✔
551
                        "still have remaining amount %v",
1✔
552
                        paymentsdb.ErrPaymentInternal, m.State.RemainingAmt)
1✔
553
        }
1✔
554

555
        // Now check if we can register a new HTLC.
556
        err := m.Registrable()
33✔
557
        if err != nil {
40✔
558
                log.Warnf("Payment(%v): cannot register HTLC attempt: %v, "+
7✔
559
                        "current status: %s", m.Info.PaymentIdentifier,
7✔
560
                        err, m.Status)
7✔
561

7✔
562
                return false, nil
7✔
563
        }
7✔
564

565
        // Now we know we can register new HTLCs.
566
        return true, nil
26✔
567
}
568

569
// serializeHTLCSettleInfo serializes the details of a settled htlc.
570
func serializeHTLCSettleInfo(w io.Writer, s *HTLCSettleInfo) error {
16✔
571
        if _, err := w.Write(s.Preimage[:]); err != nil {
16✔
572
                return err
×
573
        }
×
574

575
        if err := serializeTime(w, s.SettleTime); err != nil {
16✔
576
                return err
×
577
        }
×
578

579
        return nil
16✔
580
}
581

582
// deserializeHTLCSettleInfo deserializes the details of a settled htlc.
583
func deserializeHTLCSettleInfo(r io.Reader) (*HTLCSettleInfo, error) {
83✔
584
        s := &HTLCSettleInfo{}
83✔
585
        if _, err := io.ReadFull(r, s.Preimage[:]); err != nil {
83✔
586
                return nil, err
×
587
        }
×
588

589
        var err error
83✔
590
        s.SettleTime, err = deserializeTime(r)
83✔
591
        if err != nil {
83✔
592
                return nil, err
×
593
        }
×
594

595
        return s, nil
83✔
596
}
597

598
// serializeHTLCFailInfo serializes the details of a failed htlc including the
599
// wire failure.
600
func serializeHTLCFailInfo(w io.Writer, f *HTLCFailInfo) error {
31✔
601
        if err := serializeTime(w, f.FailTime); err != nil {
31✔
602
                return err
×
603
        }
×
604

605
        // Write failure. If there is no failure message, write an empty
606
        // byte slice.
607
        var messageBytes bytes.Buffer
31✔
608
        if f.Message != nil {
31✔
609
                err := lnwire.EncodeFailureMessage(&messageBytes, f.Message, 0)
×
610
                if err != nil {
×
611
                        return err
×
612
                }
×
613
        }
614
        if err := wire.WriteVarBytes(w, 0, messageBytes.Bytes()); err != nil {
31✔
615
                return err
×
616
        }
×
617

618
        return WriteElements(w, byte(f.Reason), f.FailureSourceIndex)
31✔
619
}
620

621
// deserializeHTLCFailInfo deserializes the details of a failed htlc including
622
// the wire failure.
623
func deserializeHTLCFailInfo(r io.Reader) (*HTLCFailInfo, error) {
291✔
624
        f := &HTLCFailInfo{}
291✔
625
        var err error
291✔
626
        f.FailTime, err = deserializeTime(r)
291✔
627
        if err != nil {
291✔
628
                return nil, err
×
629
        }
×
630

631
        // Read failure.
632
        failureBytes, err := wire.ReadVarBytes(
291✔
633
                r, 0, math.MaxUint16, "failure",
291✔
634
        )
291✔
635
        if err != nil {
291✔
636
                return nil, err
×
637
        }
×
638
        if len(failureBytes) > 0 {
291✔
639
                f.Message, err = lnwire.DecodeFailureMessage(
×
640
                        bytes.NewReader(failureBytes), 0,
×
641
                )
×
642
                if err != nil {
×
643
                        return nil, err
×
644
                }
×
645
        }
646

647
        var reason byte
291✔
648
        err = ReadElements(r, &reason, &f.FailureSourceIndex)
291✔
649
        if err != nil {
291✔
650
                return nil, err
×
651
        }
×
652
        f.Reason = HTLCFailReason(reason)
291✔
653

291✔
654
        return f, nil
291✔
655
}
656

657
// deserializeTime deserializes time as unix nanoseconds.
658
func deserializeTime(r io.Reader) (time.Time, error) {
1,665✔
659
        var scratch [8]byte
1,665✔
660
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
1,665✔
661
                return time.Time{}, err
×
662
        }
×
663

664
        // Convert to time.Time. Interpret unix nano time zero as a zero
665
        // time.Time value.
666
        unixNano := byteOrder.Uint64(scratch[:])
1,665✔
667
        if unixNano == 0 {
2,700✔
668
                return time.Time{}, nil
1,035✔
669
        }
1,035✔
670

671
        return time.Unix(0, int64(unixNano)), nil
630✔
672
}
673

674
// serializeTime serializes time as unix nanoseconds.
675
func serializeTime(w io.Writer, t time.Time) error {
293✔
676
        var scratch [8]byte
293✔
677

293✔
678
        // Convert to unix nano seconds, but only if time is non-zero. Calling
293✔
679
        // UnixNano() on a zero time yields an undefined result.
293✔
680
        var unixNano int64
293✔
681
        if !t.IsZero() {
469✔
682
                unixNano = t.UnixNano()
176✔
683
        }
176✔
684

685
        byteOrder.PutUint64(scratch[:], uint64(unixNano))
293✔
686
        _, err := w.Write(scratch[:])
293✔
687
        return err
293✔
688
}
689

690
// generateSphinxPacket generates then encodes a sphinx packet which encodes
691
// the onion route specified by the passed layer 3 route. The blob returned
692
// from this function can immediately be included within an HTLC add packet to
693
// be sent to the first hop within the route.
694
func generateSphinxPacket(rt *route.Route, paymentHash []byte,
695
        sessionKey *btcec.PrivateKey) ([]byte, *sphinx.Circuit, error) {
214✔
696

214✔
697
        // Now that we know we have an actual route, we'll map the route into a
214✔
698
        // sphinx payment path which includes per-hop payloads for each hop
214✔
699
        // that give each node within the route the necessary information
214✔
700
        // (fees, CLTV value, etc.) to properly forward the payment.
214✔
701
        sphinxPath, err := rt.ToSphinxPath()
214✔
702
        if err != nil {
216✔
703
                return nil, nil, err
2✔
704
        }
2✔
705

706
        log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v",
212✔
707
                paymentHash, lnutils.NewLogClosure(func() string {
212✔
708
                        path := make(
×
709
                                []sphinx.OnionHop, sphinxPath.TrueRouteLength(),
×
710
                        )
×
711
                        for i := range path {
×
712
                                hopCopy := sphinxPath[i]
×
713
                                path[i] = hopCopy
×
714
                        }
×
715

716
                        return spew.Sdump(path)
×
717
                }),
718
        )
719

720
        // Next generate the onion routing packet which allows us to perform
721
        // privacy preserving source routing across the network.
722
        sphinxPacket, err := sphinx.NewOnionPacket(
212✔
723
                sphinxPath, sessionKey, paymentHash,
212✔
724
                sphinx.DeterministicPacketFiller,
212✔
725
        )
212✔
726
        if err != nil {
212✔
727
                return nil, nil, err
×
728
        }
×
729

730
        // Finally, encode Sphinx packet using its wire representation to be
731
        // included within the HTLC add packet.
732
        var onionBlob bytes.Buffer
212✔
733
        if err := sphinxPacket.Encode(&onionBlob); err != nil {
212✔
734
                return nil, nil, err
×
735
        }
×
736

737
        log.Tracef("Generated sphinx packet: %v",
212✔
738
                lnutils.NewLogClosure(func() string {
212✔
739
                        // We make a copy of the ephemeral key and unset the
×
740
                        // internal curve here in order to keep the logs from
×
741
                        // getting noisy.
×
742
                        key := *sphinxPacket.EphemeralKey
×
743
                        packetCopy := *sphinxPacket
×
744
                        packetCopy.EphemeralKey = &key
×
745

×
746
                        return spew.Sdump(packetCopy)
×
747
                }),
×
748
        )
749

750
        return onionBlob.Bytes(), &sphinx.Circuit{
212✔
751
                SessionKey:  sessionKey,
212✔
752
                PaymentPath: sphinxPath.NodeKeys(),
212✔
753
        }, nil
212✔
754
}
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