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

lightningnetwork / lnd / 12986279612

27 Jan 2025 09:51AM UTC coverage: 57.652% (-1.1%) from 58.788%
12986279612

Pull #9447

github

yyforyongyu
sweep: rename methods for clarity

We now rename "third party" to "unknown" as the inputs can be spent via
an older sweeping tx, a third party (anchor), or a remote party (pin).
In fee bumper we don't have the info to distinguish the above cases, and
leave them to be further handled by the sweeper as it has more context.
Pull Request #9447: sweep: start tracking input spending status in the fee bumper

83 of 87 new or added lines in 2 files covered. (95.4%)

19578 existing lines in 256 files now uncovered.

103448 of 179434 relevant lines covered (57.65%)

24884.58 hits per line

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

87.59
/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 {
203✔
54

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

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

203✔
67
        return &HTLCAttempt{HTLCAttemptInfo: info}
203✔
68
}
203✔
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 {
71✔
73
        if h.cachedSessionKey == nil {
81✔
74
                h.cachedSessionKey, _ = btcec.PrivKeyFromBytes(
10✔
75
                        h.sessionKey[:],
10✔
76
                )
10✔
77
        }
10✔
78

79
        return h.cachedSessionKey
71✔
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 {
53✔
215
        // If the payment is in terminal state, it cannot be updated.
53✔
216
        return m.Status.updatable() != nil
53✔
217
}
53✔
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) {
740✔
223
        for _, h := range m.HTLCs {
1,491✔
224
                if h.Settle != nil {
858✔
225
                        return &h, nil
107✔
226
                }
107✔
227
        }
228

229
        return nil, m.FailureReason
633✔
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) {
781✔
235
        var sent, fees lnwire.MilliSatoshi
781✔
236
        for _, h := range m.HTLCs {
1,599✔
237
                if h.Failure != nil {
1,169✔
238
                        continue
351✔
239
                }
240

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

247
        return sent, fees
781✔
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 {
800✔
253
        var inflights []HTLCAttempt
800✔
254
        for _, h := range m.HTLCs {
1,625✔
255
                if h.Settle != nil || h.Failure != nil {
1,273✔
256
                        continue
448✔
257
                }
258

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

262
        return inflights
800✔
263
}
264

265
// GetAttempt returns the specified htlc attempt on the payment.
266
func (m *MPPayment) GetAttempt(id uint64) (*HTLCAttempt, error) {
6✔
267
        // TODO(yy): iteration can be slow, make it into a tree or use BS.
6✔
268
        for _, htlc := range m.HTLCs {
12✔
269
                htlc := htlc
6✔
270
                if htlc.AttemptID == id {
12✔
271
                        return &htlc, nil
6✔
272
                }
6✔
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 {
127✔
283
        // If updating the payment is not allowed, we can't register new HTLCs.
127✔
284
        // Otherwise, the status must be either `StatusInitiated` or
127✔
285
        // `StatusInFlight`.
127✔
286
        if err := m.Status.updatable(); err != nil {
143✔
287
                return err
16✔
288
        }
16✔
289

290
        // Exit early if this is not inflight.
291
        if m.Status != StatusInFlight {
157✔
292
                return nil
46✔
293
        }
46✔
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 {
72✔
299
                return ErrPaymentPendingSettled
7✔
300
        }
7✔
301

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

307
        // Otherwise we can add more HTLCs.
308
        return nil
52✔
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 {
719✔
314
        // Fetch the total amount and fees that has already been sent in
719✔
315
        // settled and still in-flight shards.
719✔
316
        sentAmt, fees := m.SentAmt()
719✔
317

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

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

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

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

718✔
344
        return nil
718✔
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.
352
func (m *MPPayment) SetState() error {
75✔
353
        return m.setState()
75✔
354
}
75✔
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) {
45✔
360
        // Check when the remainingAmt is not zero, which means we have more
45✔
361
        // money to be sent.
45✔
362
        if m.State.RemainingAmt != 0 {
54✔
363
                switch m.Status {
9✔
364
                // If the payment is newly created, no need to wait for HTLC
365
                // results.
366
                case StatusInitiated:
1✔
367
                        return false, nil
1✔
368

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

1✔
381
                                return true, nil
1✔
382
                        }
1✔
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.
387
                        if m.State.PaymentFailed {
3✔
388
                                return true, nil
1✔
389
                        }
1✔
390

391
                        // Otherwise we don't need to wait for inflight HTLCs
392
                        // since we still have money to be sent.
393
                        return false, nil
1✔
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.
398
                case StatusSucceeded:
1✔
399
                        return false, fmt.Errorf("%w: parts of the payment "+
1✔
400
                                "already succeeded but still have remaining "+
1✔
401
                                "amount %v", ErrPaymentInternal,
1✔
402
                                m.State.RemainingAmt)
1✔
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.
410
                default:
1✔
411
                        return false, fmt.Errorf("%w: %s",
1✔
412
                                ErrUnknownPaymentStatus, m.Status)
1✔
413
                }
414
        }
415

416
        // Now we determine whether we need to wait when the remainingAmt is
417
        // already zero.
418
        switch m.Status {
36✔
419
        // When the payment is newly created, yet the payment has no remaining
420
        // amount, return an error.
421
        case StatusInitiated:
1✔
422
                return false, fmt.Errorf("%w: %v", ErrPaymentInternal, m.Status)
1✔
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:
22✔
431
                return true, nil
22✔
432

433
        // If the payment is already succeeded, no need to wait.
434
        case StatusSucceeded:
11✔
435
                return false, nil
11✔
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.
442
        case StatusFailed:
1✔
443
                return false, fmt.Errorf("%w: %v", ErrPaymentInternal, m.Status)
1✔
444

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

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

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

462
// GetPayment returns all the HTLCs for this payment.
463
func (m *MPPayment) GetHTLCs() []HTLCAttempt {
1✔
464
        return m.HTLCs
1✔
465
}
1✔
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) {
72✔
471
        // Now check whether the remainingAmt is zero or not. If we don't have
72✔
472
        // any remainingAmt, no more HTLCs should be made.
72✔
473
        if m.State.RemainingAmt == 0 {
110✔
474
                // If the payment is newly created, yet we don't have any
38✔
475
                // remainingAmt, return an error.
38✔
476
                if m.Status == StatusInitiated {
39✔
477
                        return false, fmt.Errorf("%w: initiated payment has "+
1✔
478
                                "zero remainingAmt", ErrPaymentInternal)
1✔
479
                }
1✔
480

481
                // Otherwise, exit early since all other statuses with zero
482
                // remainingAmt indicate no more HTLCs can be made.
483
                return false, nil
37✔
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 {
35✔
493
                return false, fmt.Errorf("%w: payment already succeeded but "+
1✔
494
                        "still have remaining amount %v", ErrPaymentInternal,
1✔
495
                        m.State.RemainingAmt)
1✔
496
        }
1✔
497

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

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

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

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

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

522
        return nil
16✔
523
}
524

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

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

538
        return s, nil
89✔
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 {
31✔
544
        if err := serializeTime(w, f.FailTime); err != nil {
31✔
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
31✔
551
        if f.Message != nil {
31✔
UNCOV
552
                err := lnwire.EncodeFailureMessage(&messageBytes, f.Message, 0)
×
UNCOV
553
                if err != nil {
×
554
                        return err
×
555
                }
×
556
        }
557
        if err := wire.WriteVarBytes(w, 0, messageBytes.Bytes()); err != nil {
31✔
558
                return err
×
559
        }
×
560

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

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

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

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

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

600
// deserializeTime deserializes time as unix nanoseconds.
601
func deserializeTime(r io.Reader) (time.Time, error) {
1,741✔
602
        var scratch [8]byte
1,741✔
603
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
1,741✔
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[:])
1,741✔
610
        if unixNano == 0 {
2,836✔
611
                return time.Time{}, nil
1,095✔
612
        }
1,095✔
613

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

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

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

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