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

lightningnetwork / lnd / 13566028875

27 Feb 2025 12:09PM UTC coverage: 49.396% (-9.4%) from 58.748%
13566028875

Pull #9555

github

ellemouton
graph/db: populate the graph cache in Start instead of during construction

In this commit, we move the graph cache population logic out of the
ChannelGraph constructor and into its Start method instead.
Pull Request #9555: graph: extract cache from CRUD [6]

34 of 54 new or added lines in 4 files covered. (62.96%)

27464 existing lines in 436 files now uncovered.

101095 of 204664 relevant lines covered (49.4%)

1.54 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