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

lightningnetwork / lnd / 13440912774

20 Feb 2025 05:14PM UTC coverage: 57.697% (-1.1%) from 58.802%
13440912774

Pull #9535

github

guggero
GitHub: remove duplicate caching

Turns out that actions/setup-go starting with @v4 also adds caching.
With that, our cache size on disk has almost doubled, leading to the
GitHub runner running out of space in certain situation.
We fix that by disabling the automated caching since we already have our
own, custom-tailored version.
Pull Request #9535: GitHub: remove duplicate caching

103519 of 179417 relevant lines covered (57.7%)

24825.3 hits per line

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

82.54
/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) {
203✔
64

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

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

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

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

93
        return h.cachedSessionKey
214✔
94
}
95

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

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

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

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

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

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

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

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

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

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

315
        return inflights
799✔
316
}
317

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

343
        // Exit early if this is not inflight.
344
        if m.Status != StatusInFlight {
157✔
345
                return nil
46✔
346
        }
46✔
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 {
72✔
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 {
64✔
357
                return ErrPaymentPendingFailed
6✔
358
        }
6✔
359

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

718✔
371
        // Sanity check we haven't sent a value larger than the payment amount.
718✔
372
        totalAmt := m.Info.Value
718✔
373
        if sentAmt > totalAmt {
719✔
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()
717✔
380

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

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

717✔
397
        return nil
717✔
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) {
45✔
413
        // Check when the remainingAmt is not zero, which means we have more
45✔
414
        // money to be sent.
45✔
415
        if m.State.RemainingAmt != 0 {
54✔
416
                switch m.Status {
9✔
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:
3✔
460
                        return false, nil
3✔
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 {
36✔
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:
22✔
484
                return true, nil
22✔
485

486
        // If the payment is already succeeded, no need to wait.
487
        case StatusSucceeded:
11✔
488
                return false, nil
11✔
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 {
57✔
507
        return m.State
57✔
508
}
57✔
509

510
// Status returns the current status of the payment.
511
func (m *MPPayment) GetStatus() PaymentStatus {
158✔
512
        return m.Status
158✔
513
}
158✔
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) {
72✔
524
        // Now check whether the remainingAmt is zero or not. If we don't have
72✔
525
        // any remainingAmt, no more HTLCs should be made.
72✔
526
        if m.State.RemainingAmt == 0 {
110✔
527
                // If the payment is newly created, yet we don't have any
38✔
528
                // remainingAmt, return an error.
38✔
529
                if m.Status == StatusInitiated {
39✔
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
37✔
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 {
35✔
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()
33✔
553
        if err != nil {
40✔
554
                log.Warnf("Payment(%v): cannot register HTLC attempt: %v, "+
7✔
555
                        "current status: %s", m.Info.PaymentIdentifier,
7✔
556
                        err, m.Status)
7✔
557

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

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

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

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

575
        return nil
16✔
576
}
577

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

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

591
        return s, nil
89✔
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 {
31✔
597
        if err := serializeTime(w, f.FailTime); err != nil {
31✔
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
31✔
604
        if f.Message != nil {
31✔
605
                err := lnwire.EncodeFailureMessage(&messageBytes, f.Message, 0)
×
606
                if err != nil {
×
607
                        return err
×
608
                }
×
609
        }
610
        if err := wire.WriteVarBytes(w, 0, messageBytes.Bytes()); err != nil {
31✔
611
                return err
×
612
        }
×
613

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

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

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

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

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

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

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

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

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

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

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

702
        log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v",
211✔
703
                paymentHash, lnutils.NewLogClosure(func() string {
211✔
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(
211✔
719
                sphinxPath, sessionKey, paymentHash,
211✔
720
                sphinx.DeterministicPacketFiller,
211✔
721
        )
211✔
722
        if err != nil {
211✔
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
211✔
729
        if err := sphinxPacket.Encode(&onionBlob); err != nil {
211✔
730
                return nil, nil, err
×
731
        }
×
732

733
        log.Tracef("Generated sphinx packet: %v",
211✔
734
                lnutils.NewLogClosure(func() string {
211✔
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{
211✔
747
                SessionKey:  sessionKey,
211✔
748
                PaymentPath: sphinxPath.NodeKeys(),
211✔
749
        }, nil
211✔
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