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

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 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 {
89✔
459
        return m.Status
89✔
460
}
89✔
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