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

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 hits per line

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

69.17
/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/lightningnetwork/lnd/lntypes"
14
        "github.com/lightningnetwork/lnd/lnwire"
15
        "github.com/lightningnetwork/lnd/routing/route"
16
)
17

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

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

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

36
        // Route is the route attempted to send the HTLC.
37
        Route route.Route
38

39
        // AttemptTime is the time at which this HTLC was attempted.
40
        AttemptTime time.Time
41

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

50
// NewHtlcAttempt creates a htlc attempt.
51
func NewHtlcAttempt(attemptID uint64, sessionKey *btcec.PrivateKey,
52
        route route.Route, attemptTime time.Time,
53
        hash *lntypes.Hash) *HTLCAttempt {
3✔
54

3✔
55
        var scratch [btcec.PrivKeyBytesLen]byte
3✔
56
        copy(scratch[:], sessionKey.Serialize())
3✔
57

3✔
58
        info := HTLCAttemptInfo{
3✔
59
                AttemptID:        attemptID,
3✔
60
                sessionKey:       scratch,
3✔
61
                cachedSessionKey: sessionKey,
3✔
62
                Route:            route,
3✔
63
                AttemptTime:      attemptTime,
3✔
64
                Hash:             hash,
3✔
65
        }
3✔
66

3✔
67
        return &HTLCAttempt{HTLCAttemptInfo: info}
3✔
68
}
3✔
69

70
// SessionKey returns the ephemeral key used for a htlc attempt. This function
71
// performs expensive ec-ops to obtain the session key if it is not cached.
72
func (h *HTLCAttemptInfo) SessionKey() *btcec.PrivateKey {
3✔
73
        if h.cachedSessionKey == nil {
6✔
74
                h.cachedSessionKey, _ = btcec.PrivKeyFromBytes(
3✔
75
                        h.sessionKey[:],
3✔
76
                )
3✔
77
        }
3✔
78

79
        return h.cachedSessionKey
3✔
80
}
81

82
// HTLCAttempt contains information about a specific HTLC attempt for a given
83
// payment. It contains the HTLCAttemptInfo used to send the HTLC, as well
84
// as a timestamp and any known outcome of the attempt.
85
type HTLCAttempt struct {
86
        HTLCAttemptInfo
87

88
        // Settle is the preimage of a successful payment. This serves as a
89
        // proof of payment. It will only be non-nil for settled payments.
90
        //
91
        // NOTE: Can be nil if payment is not settled.
92
        Settle *HTLCSettleInfo
93

94
        // Fail is a failure reason code indicating the reason the payment
95
        // failed. It is only non-nil for failed payments.
96
        //
97
        // NOTE: Can be nil if payment is not failed.
98
        Failure *HTLCFailInfo
99
}
100

101
// HTLCSettleInfo encapsulates the information that augments an HTLCAttempt in
102
// the event that the HTLC is successful.
103
type HTLCSettleInfo struct {
104
        // Preimage is the preimage of a successful HTLC. This serves as a proof
105
        // of payment.
106
        Preimage lntypes.Preimage
107

108
        // SettleTime is the time at which this HTLC was settled.
109
        SettleTime time.Time
110
}
111

112
// HTLCFailReason is the reason an htlc failed.
113
type HTLCFailReason byte
114

115
const (
116
        // HTLCFailUnknown is recorded for htlcs that failed with an unknown
117
        // reason.
118
        HTLCFailUnknown HTLCFailReason = 0
119

120
        // HTLCFailUnknown is recorded for htlcs that had a failure message that
121
        // couldn't be decrypted.
122
        HTLCFailUnreadable HTLCFailReason = 1
123

124
        // HTLCFailInternal is recorded for htlcs that failed because of an
125
        // internal error.
126
        HTLCFailInternal HTLCFailReason = 2
127

128
        // HTLCFailMessage is recorded for htlcs that failed with a network
129
        // failure message.
130
        HTLCFailMessage HTLCFailReason = 3
131
)
132

133
// HTLCFailInfo encapsulates the information that augments an HTLCAttempt in the
134
// event that the HTLC fails.
135
type HTLCFailInfo struct {
136
        // FailTime is the time at which this HTLC was failed.
137
        FailTime time.Time
138

139
        // Message is the wire message that failed this HTLC. This field will be
140
        // populated when the failure reason is HTLCFailMessage.
141
        Message lnwire.FailureMessage
142

143
        // Reason is the failure reason for this HTLC.
144
        Reason HTLCFailReason
145

146
        // The position in the path of the intermediate or final node that
147
        // generated the failure message. Position zero is the sender node. This
148
        // field will be populated when the failure reason is either
149
        // HTLCFailMessage or HTLCFailUnknown.
150
        FailureSourceIndex uint32
151
}
152

153
// MPPaymentState wraps a series of info needed for a given payment, which is
154
// used by both MPP and AMP. This is a memory representation of the payment's
155
// current state and is updated whenever the payment is read from disk.
156
type MPPaymentState struct {
157
        // NumAttemptsInFlight specifies the number of HTLCs the payment is
158
        // waiting results for.
159
        NumAttemptsInFlight int
160

161
        // RemainingAmt specifies how much more money to be sent.
162
        RemainingAmt lnwire.MilliSatoshi
163

164
        // FeesPaid specifies the total fees paid so far that can be used to
165
        // calculate remaining fee budget.
166
        FeesPaid lnwire.MilliSatoshi
167

168
        // HasSettledHTLC is true if at least one of the payment's HTLCs is
169
        // settled.
170
        HasSettledHTLC bool
171

172
        // PaymentFailed is true if the payment has been marked as failed with
173
        // a reason.
174
        PaymentFailed bool
175
}
176

177
// MPPayment is a wrapper around a payment's PaymentCreationInfo and
178
// HTLCAttempts. All payments will have the PaymentCreationInfo set, any
179
// HTLCs made in attempts to be completed will populated in the HTLCs slice.
180
// Each populated HTLCAttempt represents an attempted HTLC, each of which may
181
// have the associated Settle or Fail struct populated if the HTLC is no longer
182
// in-flight.
183
type MPPayment struct {
184
        // SequenceNum is a unique identifier used to sort the payments in
185
        // order of creation.
186
        SequenceNum uint64
187

188
        // Info holds all static information about this payment, and is
189
        // populated when the payment is initiated.
190
        Info *PaymentCreationInfo
191

192
        // HTLCs holds the information about individual HTLCs that we send in
193
        // order to make the payment.
194
        HTLCs []HTLCAttempt
195

196
        // FailureReason is the failure reason code indicating the reason the
197
        // payment failed.
198
        //
199
        // NOTE: Will only be set once the daemon has given up on the payment
200
        // altogether.
201
        FailureReason *FailureReason
202

203
        // Status is the current PaymentStatus of this payment.
204
        Status PaymentStatus
205

206
        // State is the current state of the payment that holds a number of key
207
        // insights and is used to determine what to do on each payment loop
208
        // iteration.
209
        State *MPPaymentState
210
}
211

212
// Terminated returns a bool to specify whether the payment is in a terminal
213
// state.
214
func (m *MPPayment) Terminated() bool {
3✔
215
        // If the payment is in terminal state, it cannot be updated.
3✔
216
        return m.Status.updatable() != nil
3✔
217
}
3✔
218

219
// TerminalInfo returns any HTLC settle info recorded. If no settle info is
220
// recorded, any payment level failure will be returned. If neither a settle
221
// nor a failure is recorded, both return values will be nil.
222
func (m *MPPayment) TerminalInfo() (*HTLCAttempt, *FailureReason) {
3✔
223
        for _, h := range m.HTLCs {
6✔
224
                if h.Settle != nil {
6✔
225
                        return &h, nil
3✔
226
                }
3✔
227
        }
228

229
        return nil, m.FailureReason
3✔
230
}
231

232
// SentAmt returns the sum of sent amount and fees for HTLCs that are either
233
// settled or still in flight.
234
func (m *MPPayment) SentAmt() (lnwire.MilliSatoshi, lnwire.MilliSatoshi) {
3✔
235
        var sent, fees lnwire.MilliSatoshi
3✔
236
        for _, h := range m.HTLCs {
6✔
237
                if h.Failure != nil {
6✔
238
                        continue
3✔
239
                }
240

241
                // The attempt was not failed, meaning the amount was
242
                // potentially sent to the receiver.
243
                sent += h.Route.ReceiverAmt()
3✔
244
                fees += h.Route.TotalFees()
3✔
245
        }
246

247
        return sent, fees
3✔
248
}
249

250
// InFlightHTLCs returns the HTLCs that are still in-flight, meaning they have
251
// not been settled or failed.
252
func (m *MPPayment) InFlightHTLCs() []HTLCAttempt {
3✔
253
        var inflights []HTLCAttempt
3✔
254
        for _, h := range m.HTLCs {
6✔
255
                if h.Settle != nil || h.Failure != nil {
6✔
256
                        continue
3✔
257
                }
258

259
                inflights = append(inflights, h)
3✔
260
        }
261

262
        return inflights
3✔
263
}
264

265
// GetAttempt returns the specified htlc attempt on the payment.
266
func (m *MPPayment) GetAttempt(id uint64) (*HTLCAttempt, error) {
3✔
267
        // TODO(yy): iteration can be slow, make it into a tree or use BS.
3✔
268
        for _, htlc := range m.HTLCs {
6✔
269
                htlc := htlc
3✔
270
                if htlc.AttemptID == id {
6✔
271
                        return &htlc, nil
3✔
272
                }
3✔
273
        }
274

275
        return nil, errors.New("htlc attempt not found on payment")
×
276
}
277

278
// Registrable returns an error to specify whether adding more HTLCs to the
279
// payment with its current status is allowed. A payment can accept new HTLC
280
// registrations when it's newly created, or none of its HTLCs is in a terminal
281
// state.
282
func (m *MPPayment) Registrable() error {
3✔
283
        // If updating the payment is not allowed, we can't register new HTLCs.
3✔
284
        // Otherwise, the status must be either `StatusInitiated` or
3✔
285
        // `StatusInFlight`.
3✔
286
        if err := m.Status.updatable(); err != nil {
6✔
287
                return err
3✔
288
        }
3✔
289

290
        // Exit early if this is not inflight.
291
        if m.Status != StatusInFlight {
6✔
292
                return nil
3✔
293
        }
3✔
294

295
        // There are still inflight HTLCs and we need to check whether there
296
        // are settled HTLCs or the payment is failed. If we already have
297
        // settled HTLCs, we won't allow adding more HTLCs.
298
        if m.State.HasSettledHTLC {
3✔
UNCOV
299
                return ErrPaymentPendingSettled
×
UNCOV
300
        }
×
301

302
        // If the payment is already failed, we won't allow adding more HTLCs.
303
        if m.State.PaymentFailed {
3✔
UNCOV
304
                return ErrPaymentPendingFailed
×
UNCOV
305
        }
×
306

307
        // Otherwise we can add more HTLCs.
308
        return nil
3✔
309
}
310

311
// setState creates and attaches a new MPPaymentState to the payment. It also
312
// updates the payment's status based on its current state.
313
func (m *MPPayment) setState() error {
3✔
314
        // Fetch the total amount and fees that has already been sent in
3✔
315
        // settled and still in-flight shards.
3✔
316
        sentAmt, fees := m.SentAmt()
3✔
317

3✔
318
        // Sanity check we haven't sent a value larger than the payment amount.
3✔
319
        totalAmt := m.Info.Value
3✔
320
        if sentAmt > totalAmt {
3✔
UNCOV
321
                return fmt.Errorf("%w: sent=%v, total=%v", ErrSentExceedsTotal,
×
UNCOV
322
                        sentAmt, totalAmt)
×
UNCOV
323
        }
×
324

325
        // Get any terminal info for this payment.
326
        settle, failure := m.TerminalInfo()
3✔
327

3✔
328
        // Now determine the payment's status.
3✔
329
        status, err := decidePaymentStatus(m.HTLCs, m.FailureReason)
3✔
330
        if err != nil {
3✔
331
                return err
×
332
        }
×
333

334
        // Update the payment state and status.
335
        m.State = &MPPaymentState{
3✔
336
                NumAttemptsInFlight: len(m.InFlightHTLCs()),
3✔
337
                RemainingAmt:        totalAmt - sentAmt,
3✔
338
                FeesPaid:            fees,
3✔
339
                HasSettledHTLC:      settle != nil,
3✔
340
                PaymentFailed:       failure != nil,
3✔
341
        }
3✔
342
        m.Status = status
3✔
343

3✔
344
        return nil
3✔
345
}
346

347
// SetState calls the internal method setState. This is a temporary method
348
// to be used by the tests in routing. Once the tests are updated to use mocks,
349
// this method can be removed.
350
//
351
// TODO(yy): delete.
UNCOV
352
func (m *MPPayment) SetState() error {
×
UNCOV
353
        return m.setState()
×
UNCOV
354
}
×
355

356
// NeedWaitAttempts decides whether we need to hold creating more HTLC attempts
357
// and wait for the results of the payment's inflight HTLCs. Return an error if
358
// the payment is in an unexpected state.
359
func (m *MPPayment) NeedWaitAttempts() (bool, error) {
3✔
360
        // Check when the remainingAmt is not zero, which means we have more
3✔
361
        // money to be sent.
3✔
362
        if m.State.RemainingAmt != 0 {
6✔
363
                switch m.Status {
3✔
364
                // If the payment is newly created, no need to wait for HTLC
365
                // results.
UNCOV
366
                case StatusInitiated:
×
UNCOV
367
                        return false, nil
×
368

369
                // If we have inflight HTLCs, we'll check if we have terminal
370
                // states to decide if we need to wait.
UNCOV
371
                case StatusInFlight:
×
UNCOV
372
                        // We still have money to send, and one of the HTLCs is
×
UNCOV
373
                        // settled. We'd stop sending money and wait for all
×
UNCOV
374
                        // inflight HTLC attempts to finish.
×
UNCOV
375
                        if m.State.HasSettledHTLC {
×
UNCOV
376
                                log.Warnf("payment=%v has remaining amount "+
×
UNCOV
377
                                        "%v, yet at least one of its HTLCs is "+
×
UNCOV
378
                                        "settled", m.Info.PaymentIdentifier,
×
UNCOV
379
                                        m.State.RemainingAmt)
×
UNCOV
380

×
UNCOV
381
                                return true, nil
×
UNCOV
382
                        }
×
383

384
                        // The payment has a failure reason though we still
385
                        // have money to send, we'd stop sending money and wait
386
                        // for all inflight HTLC attempts to finish.
UNCOV
387
                        if m.State.PaymentFailed {
×
UNCOV
388
                                return true, nil
×
UNCOV
389
                        }
×
390

391
                        // Otherwise we don't need to wait for inflight HTLCs
392
                        // since we still have money to be sent.
UNCOV
393
                        return false, nil
×
394

395
                // We need to send more money, yet the payment is already
396
                // succeeded. Return an error in this case as the receiver is
397
                // violating the protocol.
UNCOV
398
                case StatusSucceeded:
×
UNCOV
399
                        return false, fmt.Errorf("%w: parts of the payment "+
×
UNCOV
400
                                "already succeeded but still have remaining "+
×
UNCOV
401
                                "amount %v", ErrPaymentInternal,
×
UNCOV
402
                                m.State.RemainingAmt)
×
403

404
                // The payment is failed and we have no inflight HTLCs, no need
405
                // to wait.
406
                case StatusFailed:
3✔
407
                        return false, nil
3✔
408

409
                // Unknown payment status.
UNCOV
410
                default:
×
UNCOV
411
                        return false, fmt.Errorf("%w: %s",
×
UNCOV
412
                                ErrUnknownPaymentStatus, m.Status)
×
413
                }
414
        }
415

416
        // Now we determine whether we need to wait when the remainingAmt is
417
        // already zero.
418
        switch m.Status {
3✔
419
        // When the payment is newly created, yet the payment has no remaining
420
        // amount, return an error.
UNCOV
421
        case StatusInitiated:
×
UNCOV
422
                return false, fmt.Errorf("%w: %v", ErrPaymentInternal, m.Status)
×
423

424
        // If the payment is inflight, we must wait.
425
        //
426
        // NOTE: an edge case is when all HTLCs are failed while the payment is
427
        // not failed we'd still be in this inflight state. However, since the
428
        // remainingAmt is zero here, it means we cannot be in that state as
429
        // otherwise the remainingAmt would not be zero.
430
        case StatusInFlight:
3✔
431
                return true, nil
3✔
432

433
        // If the payment is already succeeded, no need to wait.
434
        case StatusSucceeded:
3✔
435
                return false, nil
3✔
436

437
        // If the payment is already failed, yet the remaining amount is zero,
438
        // return an error as this indicates an error state. We will only each
439
        // this status when there are no inflight HTLCs and the payment is
440
        // marked as failed with a reason, which means the remainingAmt must
441
        // not be zero because our sentAmt is zero.
UNCOV
442
        case StatusFailed:
×
UNCOV
443
                return false, fmt.Errorf("%w: %v", ErrPaymentInternal, m.Status)
×
444

445
        // Unknown payment status.
UNCOV
446
        default:
×
UNCOV
447
                return false, fmt.Errorf("%w: %s", ErrUnknownPaymentStatus,
×
UNCOV
448
                        m.Status)
×
449
        }
450
}
451

452
// GetState returns the internal state of the payment.
453
func (m *MPPayment) GetState() *MPPaymentState {
3✔
454
        return m.State
3✔
455
}
3✔
456

457
// Status returns the current status of the payment.
458
func (m *MPPayment) GetStatus() PaymentStatus {
3✔
459
        return m.Status
3✔
460
}
3✔
461

462
// GetPayment returns all the HTLCs for this payment.
UNCOV
463
func (m *MPPayment) GetHTLCs() []HTLCAttempt {
×
UNCOV
464
        return m.HTLCs
×
UNCOV
465
}
×
466

467
// AllowMoreAttempts is used to decide whether we can safely attempt more HTLCs
468
// for a given payment state. Return an error if the payment is in an
469
// unexpected state.
470
func (m *MPPayment) AllowMoreAttempts() (bool, error) {
3✔
471
        // Now check whether the remainingAmt is zero or not. If we don't have
3✔
472
        // any remainingAmt, no more HTLCs should be made.
3✔
473
        if m.State.RemainingAmt == 0 {
6✔
474
                // If the payment is newly created, yet we don't have any
3✔
475
                // remainingAmt, return an error.
3✔
476
                if m.Status == StatusInitiated {
3✔
UNCOV
477
                        return false, fmt.Errorf("%w: initiated payment has "+
×
UNCOV
478
                                "zero remainingAmt", ErrPaymentInternal)
×
UNCOV
479
                }
×
480

481
                // Otherwise, exit early since all other statuses with zero
482
                // remainingAmt indicate no more HTLCs can be made.
483
                return false, nil
3✔
484
        }
485

486
        // Otherwise, the remaining amount is not zero, we now decide whether
487
        // to make more attempts based on the payment's current status.
488
        //
489
        // If at least one of the payment's attempts is settled, yet we haven't
490
        // sent all the amount, it indicates something is wrong with the peer
491
        // as the preimage is received. In this case, return an error state.
492
        if m.Status == StatusSucceeded {
3✔
UNCOV
493
                return false, fmt.Errorf("%w: payment already succeeded but "+
×
UNCOV
494
                        "still have remaining amount %v", ErrPaymentInternal,
×
UNCOV
495
                        m.State.RemainingAmt)
×
UNCOV
496
        }
×
497

498
        // Now check if we can register a new HTLC.
499
        err := m.Registrable()
3✔
500
        if err != nil {
6✔
501
                log.Warnf("Payment(%v): cannot register HTLC attempt: %v, "+
3✔
502
                        "current status: %s", m.Info.PaymentIdentifier,
3✔
503
                        err, m.Status)
3✔
504

3✔
505
                return false, nil
3✔
506
        }
3✔
507

508
        // Now we know we can register new HTLCs.
509
        return true, nil
3✔
510
}
511

512
// serializeHTLCSettleInfo serializes the details of a settled htlc.
513
func serializeHTLCSettleInfo(w io.Writer, s *HTLCSettleInfo) error {
3✔
514
        if _, err := w.Write(s.Preimage[:]); err != nil {
3✔
515
                return err
×
516
        }
×
517

518
        if err := serializeTime(w, s.SettleTime); err != nil {
3✔
519
                return err
×
520
        }
×
521

522
        return nil
3✔
523
}
524

525
// deserializeHTLCSettleInfo deserializes the details of a settled htlc.
526
func deserializeHTLCSettleInfo(r io.Reader) (*HTLCSettleInfo, error) {
3✔
527
        s := &HTLCSettleInfo{}
3✔
528
        if _, err := io.ReadFull(r, s.Preimage[:]); err != nil {
3✔
529
                return nil, err
×
530
        }
×
531

532
        var err error
3✔
533
        s.SettleTime, err = deserializeTime(r)
3✔
534
        if err != nil {
3✔
535
                return nil, err
×
536
        }
×
537

538
        return s, nil
3✔
539
}
540

541
// serializeHTLCFailInfo serializes the details of a failed htlc including the
542
// wire failure.
543
func serializeHTLCFailInfo(w io.Writer, f *HTLCFailInfo) error {
3✔
544
        if err := serializeTime(w, f.FailTime); err != nil {
3✔
545
                return err
×
546
        }
×
547

548
        // Write failure. If there is no failure message, write an empty
549
        // byte slice.
550
        var messageBytes bytes.Buffer
3✔
551
        if f.Message != nil {
6✔
552
                err := lnwire.EncodeFailureMessage(&messageBytes, f.Message, 0)
3✔
553
                if err != nil {
3✔
554
                        return err
×
555
                }
×
556
        }
557
        if err := wire.WriteVarBytes(w, 0, messageBytes.Bytes()); err != nil {
3✔
558
                return err
×
559
        }
×
560

561
        return WriteElements(w, byte(f.Reason), f.FailureSourceIndex)
3✔
562
}
563

564
// deserializeHTLCFailInfo deserializes the details of a failed htlc including
565
// the wire failure.
566
func deserializeHTLCFailInfo(r io.Reader) (*HTLCFailInfo, error) {
3✔
567
        f := &HTLCFailInfo{}
3✔
568
        var err error
3✔
569
        f.FailTime, err = deserializeTime(r)
3✔
570
        if err != nil {
3✔
571
                return nil, err
×
572
        }
×
573

574
        // Read failure.
575
        failureBytes, err := wire.ReadVarBytes(
3✔
576
                r, 0, math.MaxUint16, "failure",
3✔
577
        )
3✔
578
        if err != nil {
3✔
579
                return nil, err
×
580
        }
×
581
        if len(failureBytes) > 0 {
6✔
582
                f.Message, err = lnwire.DecodeFailureMessage(
3✔
583
                        bytes.NewReader(failureBytes), 0,
3✔
584
                )
3✔
585
                if err != nil {
3✔
586
                        return nil, err
×
587
                }
×
588
        }
589

590
        var reason byte
3✔
591
        err = ReadElements(r, &reason, &f.FailureSourceIndex)
3✔
592
        if err != nil {
3✔
593
                return nil, err
×
594
        }
×
595
        f.Reason = HTLCFailReason(reason)
3✔
596

3✔
597
        return f, nil
3✔
598
}
599

600
// deserializeTime deserializes time as unix nanoseconds.
601
func deserializeTime(r io.Reader) (time.Time, error) {
3✔
602
        var scratch [8]byte
3✔
603
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
3✔
604
                return time.Time{}, err
×
605
        }
×
606

607
        // Convert to time.Time. Interpret unix nano time zero as a zero
608
        // time.Time value.
609
        unixNano := byteOrder.Uint64(scratch[:])
3✔
610
        if unixNano == 0 {
3✔
UNCOV
611
                return time.Time{}, nil
×
UNCOV
612
        }
×
613

614
        return time.Unix(0, int64(unixNano)), nil
3✔
615
}
616

617
// serializeTime serializes time as unix nanoseconds.
618
func serializeTime(w io.Writer, t time.Time) error {
3✔
619
        var scratch [8]byte
3✔
620

3✔
621
        // Convert to unix nano seconds, but only if time is non-zero. Calling
3✔
622
        // UnixNano() on a zero time yields an undefined result.
3✔
623
        var unixNano int64
3✔
624
        if !t.IsZero() {
6✔
625
                unixNano = t.UnixNano()
3✔
626
        }
3✔
627

628
        byteOrder.PutUint64(scratch[:], uint64(unixNano))
3✔
629
        _, err := w.Write(scratch[:])
3✔
630
        return err
3✔
631
}
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