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

lightningnetwork / lnd / 13536249039

26 Feb 2025 03:42AM UTC coverage: 57.462% (-1.4%) from 58.835%
13536249039

Pull #8453

github

Roasbeef
peer: update chooseDeliveryScript to gen script if needed

In this commit, we update `chooseDeliveryScript` to generate a new
script if needed. This allows us to fold in a few other lines that
always followed this function into this expanded function.

The tests have been updated accordingly.
Pull Request #8453: [4/4] - multi: integrate new rbf coop close FSM into the existing peer flow

275 of 1318 new or added lines in 22 files covered. (20.86%)

19521 existing lines in 257 files now uncovered.

103858 of 180741 relevant lines covered (57.46%)

24750.23 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✔
UNCOV
605
                err := lnwire.EncodeFailureMessage(&messageBytes, f.Message, 0)
×
UNCOV
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✔
UNCOV
635
                f.Message, err = lnwire.DecodeFailureMessage(
×
UNCOV
636
                        bytes.NewReader(failureBytes), 0,
×
UNCOV
637
                )
×
UNCOV
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