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

lightningnetwork / lnd / 15736109134

18 Jun 2025 02:46PM UTC coverage: 58.197% (-10.1%) from 68.248%
15736109134

Pull #9752

github

web-flow
Merge d2634a68c into 31c74f20f
Pull Request #9752: routerrpc: reject payment to invoice that don't have payment secret or blinded paths

6 of 13 new or added lines in 2 files covered. (46.15%)

28331 existing lines in 455 files now uncovered.

97860 of 168153 relevant lines covered (58.2%)

1.81 hits per line

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

66.27
/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
        "github.com/lightningnetwork/lnd/routing/route"
19
)
20

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

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

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

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

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

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

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

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

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

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

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

3✔
77
        if err := info.attachOnionBlobAndCircuit(); err != nil {
3✔
UNCOV
78
                return nil, err
×
UNCOV
79
        }
×
80

81
        return &HTLCAttempt{HTLCAttemptInfo: info}, nil
3✔
82
}
83

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

93
        return h.cachedSessionKey
3✔
94
}
95

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

105
        return h.onionBlob, nil
3✔
106
}
107

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

116
        return h.circuit, nil
3✔
117
}
118

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

129
        copy(h.onionBlob[:], onionBlob)
3✔
130
        h.circuit = circuit
3✔
131

3✔
132
        return nil
3✔
133
}
134

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

282
        return nil, m.FailureReason
3✔
283
}
284

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

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

300
        return sent, fees
3✔
301
}
302

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

312
                inflights = append(inflights, h)
3✔
313
        }
314

315
        return inflights
3✔
316
}
317

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

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

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

343
        // Exit early if this is not inflight.
344
        if m.Status != StatusInFlight {
6✔
345
                return nil
3✔
346
        }
3✔
347

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

355
        // If the payment is already failed, we won't allow adding more HTLCs.
356
        if m.State.PaymentFailed {
3✔
UNCOV
357
                return ErrPaymentPendingFailed
×
UNCOV
358
        }
×
359

360
        // Otherwise we can add more HTLCs.
361
        return nil
3✔
362
}
363

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

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

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

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

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

3✔
397
        return nil
3✔
398
}
399

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

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

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

×
UNCOV
434
                                return true, nil
×
UNCOV
435
                        }
×
436

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

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

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

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

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

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

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

486
        // If the payment is already succeeded, no need to wait.
487
        case StatusSucceeded:
3✔
488
                return false, nil
3✔
489

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

498
        // Unknown payment status.
UNCOV
499
        default:
×
UNCOV
500
                return false, fmt.Errorf("%w: %s", ErrUnknownPaymentStatus,
×
UNCOV
501
                        m.Status)
×
502
        }
503
}
504

505
// GetState returns the internal state of the payment.
506
func (m *MPPayment) GetState() *MPPaymentState {
3✔
507
        return m.State
3✔
508
}
3✔
509

510
// Status returns the current status of the payment.
511
func (m *MPPayment) GetStatus() PaymentStatus {
3✔
512
        return m.Status
3✔
513
}
3✔
514

515
// GetPayment returns all the HTLCs for this payment.
UNCOV
516
func (m *MPPayment) GetHTLCs() []HTLCAttempt {
×
UNCOV
517
        return m.HTLCs
×
UNCOV
518
}
×
519

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

534
                // Otherwise, exit early since all other statuses with zero
535
                // remainingAmt indicate no more HTLCs can be made.
536
                return false, nil
3✔
537
        }
538

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

551
        // Now check if we can register a new HTLC.
552
        err := m.Registrable()
3✔
553
        if err != nil {
6✔
554
                log.Warnf("Payment(%v): cannot register HTLC attempt: %v, "+
3✔
555
                        "current status: %s", m.Info.PaymentIdentifier,
3✔
556
                        err, m.Status)
3✔
557

3✔
558
                return false, nil
3✔
559
        }
3✔
560

561
        // Now we know we can register new HTLCs.
562
        return true, nil
3✔
563
}
564

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

571
        if err := serializeTime(w, s.SettleTime); err != nil {
3✔
572
                return err
×
573
        }
×
574

575
        return nil
3✔
576
}
577

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

585
        var err error
3✔
586
        s.SettleTime, err = deserializeTime(r)
3✔
587
        if err != nil {
3✔
588
                return nil, err
×
589
        }
×
590

591
        return s, nil
3✔
592
}
593

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

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

614
        return WriteElements(w, byte(f.Reason), f.FailureSourceIndex)
3✔
615
}
616

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

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

643
        var reason byte
3✔
644
        err = ReadElements(r, &reason, &f.FailureSourceIndex)
3✔
645
        if err != nil {
3✔
646
                return nil, err
×
647
        }
×
648
        f.Reason = HTLCFailReason(reason)
3✔
649

3✔
650
        return f, nil
3✔
651
}
652

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

660
        // Convert to time.Time. Interpret unix nano time zero as a zero
661
        // time.Time value.
662
        unixNano := byteOrder.Uint64(scratch[:])
3✔
663
        if unixNano == 0 {
3✔
UNCOV
664
                return time.Time{}, nil
×
UNCOV
665
        }
×
666

667
        return time.Unix(0, int64(unixNano)), nil
3✔
668
}
669

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

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

681
        byteOrder.PutUint64(scratch[:], uint64(unixNano))
3✔
682
        _, err := w.Write(scratch[:])
3✔
683
        return err
3✔
684
}
685

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

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

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

712
                        return spew.Sdump(path)
×
713
                }),
714
        )
715

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

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

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

×
742
                        return spew.Sdump(packetCopy)
×
743
                }),
×
744
        )
745

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