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

lightningnetwork / lnd / 12986192663

27 Jan 2025 09:46AM UTC coverage: 58.804% (+0.03%) from 58.777%
12986192663

Pull #9150

github

yyforyongyu
itest+lntest: assert payment is failed once the htlc times out
Pull Request #9150: routing+htlcswitch: fix stuck inflight payments

186 of 234 new or added lines in 5 files covered. (79.49%)

48 existing lines in 15 files now uncovered.

136133 of 231504 relevant lines covered (58.8%)

19293.76 hits per line

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

84.32
/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) {
206✔
64

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

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

206✔
77
        if err := info.attachOnionBlobAndCircuit(); err != nil {
207✔
78
                return nil, err
1✔
79
        }
1✔
80

81
        return &HTLCAttempt{HTLCAttemptInfo: info}, nil
205✔
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 {
217✔
87
        if h.cachedSessionKey == nil {
230✔
88
                h.cachedSessionKey, _ = btcec.PrivKeyFromBytes(
13✔
89
                        h.sessionKey[:],
13✔
90
                )
13✔
91
        }
13✔
92

93
        return h.cachedSessionKey
217✔
94
}
95

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

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

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

116
        return h.circuit, nil
37✔
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 {
215✔
122
        onionBlob, circuit, err := generateSphinxPacket(
215✔
123
                &h.Route, h.Hash[:], h.SessionKey(),
215✔
124
        )
215✔
125
        if err != nil {
216✔
126
                return err
1✔
127
        }
1✔
128

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

214✔
132
        return nil
214✔
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 {
56✔
268
        // If the payment is in terminal state, it cannot be updated.
56✔
269
        return m.Status.updatable() != nil
56✔
270
}
56✔
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) {
741✔
276
        for _, h := range m.HTLCs {
1,493✔
277
                if h.Settle != nil {
862✔
278
                        return &h, nil
110✔
279
                }
110✔
280
        }
281

282
        return nil, m.FailureReason
634✔
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) {
783✔
288
        var sent, fees lnwire.MilliSatoshi
783✔
289
        for _, h := range m.HTLCs {
1,603✔
290
                if h.Failure != nil {
1,173✔
291
                        continue
353✔
292
                }
293

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

300
        return sent, fees
783✔
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 {
802✔
306
        var inflights []HTLCAttempt
802✔
307
        for _, h := range m.HTLCs {
1,629✔
308
                if h.Settle != nil || h.Failure != nil {
1,277✔
309
                        continue
450✔
310
                }
311

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

315
        return inflights
802✔
316
}
317

318
// GetAttempt returns the specified htlc attempt on the payment.
319
func (m *MPPayment) GetAttempt(id uint64) (*HTLCAttempt, error) {
9✔
320
        // TODO(yy): iteration can be slow, make it into a tree or use BS.
9✔
321
        for _, htlc := range m.HTLCs {
18✔
322
                htlc := htlc
9✔
323
                if htlc.AttemptID == id {
18✔
324
                        return &htlc, nil
9✔
325
                }
9✔
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 {
130✔
336
        // If updating the payment is not allowed, we can't register new HTLCs.
130✔
337
        // Otherwise, the status must be either `StatusInitiated` or
130✔
338
        // `StatusInFlight`.
130✔
339
        if err := m.Status.updatable(); err != nil {
149✔
340
                return err
19✔
341
        }
19✔
342

343
        // Exit early if this is not inflight.
344
        if m.Status != StatusInFlight {
163✔
345
                return nil
49✔
346
        }
49✔
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 {
75✔
352
                return ErrPaymentPendingSettled
7✔
353
        }
7✔
354

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

360
        // Otherwise we can add more HTLCs.
361
        return nil
55✔
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 {
721✔
367
        // Fetch the total amount and fees that has already been sent in
721✔
368
        // settled and still in-flight shards.
721✔
369
        sentAmt, fees := m.SentAmt()
721✔
370

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

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

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

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

720✔
397
        return nil
720✔
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.
405
func (m *MPPayment) SetState() error {
74✔
406
        return m.setState()
74✔
407
}
74✔
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) {
48✔
413
        // Check when the remainingAmt is not zero, which means we have more
48✔
414
        // money to be sent.
48✔
415
        if m.State.RemainingAmt != 0 {
60✔
416
                switch m.Status {
12✔
417
                // If the payment is newly created, no need to wait for HTLC
418
                // results.
419
                case StatusInitiated:
1✔
420
                        return false, nil
1✔
421

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

1✔
434
                                return true, nil
1✔
435
                        }
1✔
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.
440
                        if m.State.PaymentFailed {
3✔
441
                                return true, nil
1✔
442
                        }
1✔
443

444
                        // Otherwise we don't need to wait for inflight HTLCs
445
                        // since we still have money to be sent.
446
                        return false, nil
1✔
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.
451
                case StatusSucceeded:
1✔
452
                        return false, fmt.Errorf("%w: parts of the payment "+
1✔
453
                                "already succeeded but still have remaining "+
1✔
454
                                "amount %v", ErrPaymentInternal,
1✔
455
                                m.State.RemainingAmt)
1✔
456

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

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

469
        // Now we determine whether we need to wait when the remainingAmt is
470
        // already zero.
471
        switch m.Status {
39✔
472
        // When the payment is newly created, yet the payment has no remaining
473
        // amount, return an error.
474
        case StatusInitiated:
1✔
475
                return false, fmt.Errorf("%w: %v", ErrPaymentInternal, m.Status)
1✔
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:
25✔
484
                return true, nil
25✔
485

486
        // If the payment is already succeeded, no need to wait.
487
        case StatusSucceeded:
14✔
488
                return false, nil
14✔
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.
495
        case StatusFailed:
1✔
496
                return false, fmt.Errorf("%w: %v", ErrPaymentInternal, m.Status)
1✔
497

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

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

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

515
// GetPayment returns all the HTLCs for this payment.
516
func (m *MPPayment) GetHTLCs() []HTLCAttempt {
1✔
517
        return m.HTLCs
1✔
518
}
1✔
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) {
75✔
524
        // Now check whether the remainingAmt is zero or not. If we don't have
75✔
525
        // any remainingAmt, no more HTLCs should be made.
75✔
526
        if m.State.RemainingAmt == 0 {
116✔
527
                // If the payment is newly created, yet we don't have any
41✔
528
                // remainingAmt, return an error.
41✔
529
                if m.Status == StatusInitiated {
42✔
530
                        return false, fmt.Errorf("%w: initiated payment has "+
1✔
531
                                "zero remainingAmt", ErrPaymentInternal)
1✔
532
                }
1✔
533

534
                // Otherwise, exit early since all other statuses with zero
535
                // remainingAmt indicate no more HTLCs can be made.
536
                return false, nil
40✔
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 {
38✔
546
                return false, fmt.Errorf("%w: payment already succeeded but "+
1✔
547
                        "still have remaining amount %v", ErrPaymentInternal,
1✔
548
                        m.State.RemainingAmt)
1✔
549
        }
1✔
550

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

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

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

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

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

575
        return nil
19✔
576
}
577

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

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

591
        return s, nil
92✔
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 {
34✔
597
        if err := serializeTime(w, f.FailTime); err != nil {
34✔
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
34✔
604
        if f.Message != nil {
37✔
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 {
34✔
611
                return err
×
612
        }
×
613

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

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

627
        // Read failure.
628
        failureBytes, err := wire.ReadVarBytes(
308✔
629
                r, 0, math.MaxUint16, "failure",
308✔
630
        )
308✔
631
        if err != nil {
308✔
632
                return nil, err
×
633
        }
×
634
        if len(failureBytes) > 0 {
311✔
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
308✔
644
        err = ReadElements(r, &reason, &f.FailureSourceIndex)
308✔
645
        if err != nil {
308✔
646
                return nil, err
×
647
        }
×
648
        f.Reason = HTLCFailReason(reason)
308✔
649

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

653
// deserializeTime deserializes time as unix nanoseconds.
654
func deserializeTime(r io.Reader) (time.Time, error) {
1,744✔
655
        var scratch [8]byte
1,744✔
656
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
1,744✔
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[:])
1,744✔
663
        if unixNano == 0 {
2,839✔
664
                return time.Time{}, nil
1,095✔
665
        }
1,095✔
666

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

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

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

681
        byteOrder.PutUint64(scratch[:], uint64(unixNano))
296✔
682
        _, err := w.Write(scratch[:])
296✔
683
        return err
296✔
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) {
216✔
692

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

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

NEW
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(
214✔
719
                sphinxPath, sessionKey, paymentHash,
214✔
720
                sphinx.DeterministicPacketFiller,
214✔
721
        )
214✔
722
        if err != nil {
214✔
NEW
723
                return nil, nil, err
×
NEW
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
214✔
729
        if err := sphinxPacket.Encode(&onionBlob); err != nil {
214✔
NEW
730
                return nil, nil, err
×
NEW
731
        }
×
732

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

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

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