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

lightningnetwork / lnd / 14513053602

17 Apr 2025 09:56AM UTC coverage: 56.754% (-12.3%) from 69.035%
14513053602

Pull #9727

github

web-flow
Merge 5fb0f4317 into 24fdae7df
Pull Request #9727: Aux bandwidth manager: also pass HTLC blob to `ShouldHandleTraffic`

3 of 8 new or added lines in 2 files covered. (37.5%)

24357 existing lines in 290 files now uncovered.

107518 of 189445 relevant lines covered (56.75%)

22634.92 hits per line

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

71.82
/channeldb/payments.go
1
package channeldb
2

3
import (
4
        "bytes"
5
        "encoding/binary"
6
        "errors"
7
        "fmt"
8
        "io"
9
        "sort"
10
        "time"
11

12
        "github.com/btcsuite/btcd/btcec/v2"
13
        "github.com/btcsuite/btcd/wire"
14
        "github.com/lightningnetwork/lnd/kvdb"
15
        "github.com/lightningnetwork/lnd/lntypes"
16
        "github.com/lightningnetwork/lnd/lnwire"
17
        "github.com/lightningnetwork/lnd/record"
18
        "github.com/lightningnetwork/lnd/routing/route"
19
        "github.com/lightningnetwork/lnd/tlv"
20
)
21

22
var (
23
        // paymentsRootBucket is the name of the top-level bucket within the
24
        // database that stores all data related to payments. Within this
25
        // bucket, each payment hash its own sub-bucket keyed by its payment
26
        // hash.
27
        //
28
        // Bucket hierarchy:
29
        //
30
        // root-bucket
31
        //      |
32
        //      |-- <paymenthash>
33
        //      |        |--sequence-key: <sequence number>
34
        //      |        |--creation-info-key: <creation info>
35
        //      |        |--fail-info-key: <(optional) fail info>
36
        //      |        |
37
        //      |        |--payment-htlcs-bucket (shard-bucket)
38
        //      |        |        |
39
        //      |        |        |-- ai<htlc attempt ID>: <htlc attempt info>
40
        //      |        |        |-- si<htlc attempt ID>: <(optional) settle info>
41
        //      |        |        |-- fi<htlc attempt ID>: <(optional) fail info>
42
        //      |        |        |
43
        //      |        |       ...
44
        //      |        |
45
        //      |        |
46
        //      |        |--duplicate-bucket (only for old, completed payments)
47
        //      |                 |
48
        //      |                 |-- <seq-num>
49
        //      |                 |       |--sequence-key: <sequence number>
50
        //      |                 |       |--creation-info-key: <creation info>
51
        //      |                 |       |--ai: <attempt info>
52
        //      |                 |       |--si: <settle info>
53
        //      |                 |       |--fi: <fail info>
54
        //      |                 |
55
        //      |                 |-- <seq-num>
56
        //      |                 |       |
57
        //      |                ...     ...
58
        //      |
59
        //      |-- <paymenthash>
60
        //      |        |
61
        //      |       ...
62
        //     ...
63
        //
64
        paymentsRootBucket = []byte("payments-root-bucket")
65

66
        // paymentSequenceKey is a key used in the payment's sub-bucket to
67
        // store the sequence number of the payment.
68
        paymentSequenceKey = []byte("payment-sequence-key")
69

70
        // paymentCreationInfoKey is a key used in the payment's sub-bucket to
71
        // store the creation info of the payment.
72
        paymentCreationInfoKey = []byte("payment-creation-info")
73

74
        // paymentHtlcsBucket is a bucket where we'll store the information
75
        // about the HTLCs that were attempted for a payment.
76
        paymentHtlcsBucket = []byte("payment-htlcs-bucket")
77

78
        // htlcAttemptInfoKey is the key used as the prefix of an HTLC attempt
79
        // to store the info about the attempt that was done for the HTLC in
80
        // question. The HTLC attempt ID is concatenated at the end.
81
        htlcAttemptInfoKey = []byte("ai")
82

83
        // htlcSettleInfoKey is the key used as the prefix of an HTLC attempt
84
        // settle info, if any. The HTLC attempt ID is concatenated at the end.
85
        htlcSettleInfoKey = []byte("si")
86

87
        // htlcFailInfoKey is the key used as the prefix of an HTLC attempt
88
        // failure information, if any.The  HTLC attempt ID is concatenated at
89
        // the end.
90
        htlcFailInfoKey = []byte("fi")
91

92
        // paymentFailInfoKey is a key used in the payment's sub-bucket to
93
        // store information about the reason a payment failed.
94
        paymentFailInfoKey = []byte("payment-fail-info")
95

96
        // paymentsIndexBucket is the name of the top-level bucket within the
97
        // database that stores an index of payment sequence numbers to its
98
        // payment hash.
99
        // payments-sequence-index-bucket
100
        //         |--<sequence-number>: <payment hash>
101
        //         |--...
102
        //         |--<sequence-number>: <payment hash>
103
        paymentsIndexBucket = []byte("payments-index-bucket")
104
)
105

106
var (
107
        // ErrNoSequenceNumber is returned if we look up a payment which does
108
        // not have a sequence number.
109
        ErrNoSequenceNumber = errors.New("sequence number not found")
110

111
        // ErrDuplicateNotFound is returned when we lookup a payment by its
112
        // index and cannot find a payment with a matching sequence number.
113
        ErrDuplicateNotFound = errors.New("duplicate payment not found")
114

115
        // ErrNoDuplicateBucket is returned when we expect to find duplicates
116
        // when looking up a payment from its index, but the payment does not
117
        // have any.
118
        ErrNoDuplicateBucket = errors.New("expected duplicate bucket")
119

120
        // ErrNoDuplicateNestedBucket is returned if we do not find duplicate
121
        // payments in their own sub-bucket.
122
        ErrNoDuplicateNestedBucket = errors.New("nested duplicate bucket not " +
123
                "found")
124
)
125

126
// FailureReason encodes the reason a payment ultimately failed.
127
type FailureReason byte
128

129
const (
130
        // FailureReasonTimeout indicates that the payment did timeout before a
131
        // successful payment attempt was made.
132
        FailureReasonTimeout FailureReason = 0
133

134
        // FailureReasonNoRoute indicates no successful route to the
135
        // destination was found during path finding.
136
        FailureReasonNoRoute FailureReason = 1
137

138
        // FailureReasonError indicates that an unexpected error happened during
139
        // payment.
140
        FailureReasonError FailureReason = 2
141

142
        // FailureReasonPaymentDetails indicates that either the hash is unknown
143
        // or the final cltv delta or amount is incorrect.
144
        FailureReasonPaymentDetails FailureReason = 3
145

146
        // FailureReasonInsufficientBalance indicates that we didn't have enough
147
        // balance to complete the payment.
148
        FailureReasonInsufficientBalance FailureReason = 4
149

150
        // FailureReasonCanceled indicates that the payment was canceled by the
151
        // user.
152
        FailureReasonCanceled FailureReason = 5
153

154
        // TODO(joostjager): Add failure reasons for:
155
        // LocalLiquidityInsufficient, RemoteCapacityInsufficient.
156
)
157

158
// Error returns a human-readable error string for the FailureReason.
159
func (r FailureReason) Error() string {
40✔
160
        return r.String()
40✔
161
}
40✔
162

163
// String returns a human-readable FailureReason.
164
func (r FailureReason) String() string {
40✔
165
        switch r {
40✔
166
        case FailureReasonTimeout:
12✔
167
                return "timeout"
12✔
168
        case FailureReasonNoRoute:
8✔
169
                return "no_route"
8✔
170
        case FailureReasonError:
14✔
171
                return "error"
14✔
172
        case FailureReasonPaymentDetails:
2✔
173
                return "incorrect_payment_details"
2✔
UNCOV
174
        case FailureReasonInsufficientBalance:
×
UNCOV
175
                return "insufficient_balance"
×
176
        case FailureReasonCanceled:
4✔
177
                return "canceled"
4✔
178
        }
179

180
        return "unknown"
×
181
}
182

183
// PaymentCreationInfo is the information necessary to have ready when
184
// initiating a payment, moving it into state InFlight.
185
type PaymentCreationInfo struct {
186
        // PaymentIdentifier is the hash this payment is paying to in case of
187
        // non-AMP payments, and the SetID for AMP payments.
188
        PaymentIdentifier lntypes.Hash
189

190
        // Value is the amount we are paying.
191
        Value lnwire.MilliSatoshi
192

193
        // CreationTime is the time when this payment was initiated.
194
        CreationTime time.Time
195

196
        // PaymentRequest is the full payment request, if any.
197
        PaymentRequest []byte
198

199
        // FirstHopCustomRecords are the TLV records that are to be sent to the
200
        // first hop of this payment. These records will be transmitted via the
201
        // wire message only and therefore do not affect the onion payload size.
202
        FirstHopCustomRecords lnwire.CustomRecords
203
}
204

205
// htlcBucketKey creates a composite key from prefix and id where the result is
206
// simply the two concatenated.
8✔
207
func htlcBucketKey(prefix, id []byte) []byte {
8✔
208
        key := make([]byte, len(prefix)+len(id))
8✔
209
        copy(key, prefix)
8✔
210
        copy(key[len(prefix):], id)
211
        return key
212
}
213

265✔
214
// FetchPayments returns all sent payments found in the DB.
265✔
215
//
265✔
216
// nolint: dupl
265✔
217
func (d *DB) FetchPayments() ([]*MPPayment, error) {
265✔
218
        var payments []*MPPayment
265✔
219

220
        err := kvdb.View(d, func(tx kvdb.RTx) error {
221
                paymentsBucket := tx.ReadBucket(paymentsRootBucket)
222
                if paymentsBucket == nil {
223
                        return nil
41✔
224
                }
41✔
225

41✔
226
                return paymentsBucket.ForEach(func(k, v []byte) error {
82✔
227
                        bucket := paymentsBucket.NestedReadBucket(k)
41✔
228
                        if bucket == nil {
41✔
229
                                // We only expect sub-buckets to be found in
×
230
                                // this top-level bucket.
×
231
                                return fmt.Errorf("non bucket element in " +
232
                                        "payments bucket")
189✔
233
                        }
148✔
234

148✔
UNCOV
235
                        p, err := fetchPayment(bucket)
×
UNCOV
236
                        if err != nil {
×
237
                                return err
×
238
                        }
×
UNCOV
239

×
240
                        payments = append(payments, p)
241

148✔
242
                        // For older versions of lnd, duplicate payments to a
148✔
UNCOV
243
                        // payment has was possible. These will be found in a
×
UNCOV
244
                        // sub-bucket indexed by their sequence number if
×
245
                        // available.
246
                        duplicatePayments, err := fetchDuplicatePayments(bucket)
148✔
247
                        if err != nil {
148✔
248
                                return err
148✔
249
                        }
148✔
250

148✔
251
                        payments = append(payments, duplicatePayments...)
148✔
252
                        return nil
148✔
253
                })
148✔
UNCOV
254
        }, func() {
×
UNCOV
255
                payments = nil
×
256
        })
257
        if err != nil {
148✔
258
                return nil, err
148✔
259
        }
260

41✔
261
        // Before returning, sort the payments by their sequence number.
41✔
262
        sort.Slice(payments, func(i, j int) bool {
41✔
263
                return payments[i].SequenceNum < payments[j].SequenceNum
41✔
UNCOV
264
        })
×
UNCOV
265

×
266
        return payments, nil
267
}
268

278✔
269
func fetchCreationInfo(bucket kvdb.RBucket) (*PaymentCreationInfo, error) {
237✔
270
        b := bucket.Get(paymentCreationInfoKey)
237✔
271
        if b == nil {
272
                return nil, fmt.Errorf("creation info not found")
41✔
273
        }
274

275
        r := bytes.NewReader(b)
640✔
276
        return deserializePaymentCreationInfo(r)
640✔
277
}
640✔
UNCOV
278

×
UNCOV
279
func fetchPayment(bucket kvdb.RBucket) (*MPPayment, error) {
×
280
        seqBytes := bucket.Get(paymentSequenceKey)
281
        if seqBytes == nil {
640✔
282
                return nil, fmt.Errorf("sequence number not found")
640✔
283
        }
284

285
        sequenceNum := binary.BigEndian.Uint64(seqBytes)
640✔
286

640✔
287
        // Get the PaymentCreationInfo.
640✔
UNCOV
288
        creationInfo, err := fetchCreationInfo(bucket)
×
UNCOV
289
        if err != nil {
×
290
                return nil, err
291
        }
640✔
292

640✔
293
        var htlcs []HTLCAttempt
640✔
294
        htlcsBucket := bucket.NestedReadBucket(paymentHtlcsBucket)
640✔
295
        if htlcsBucket != nil {
640✔
UNCOV
296
                // Get the payment attempts. This can be empty.
×
UNCOV
297
                htlcs, err = fetchHtlcAttempts(htlcsBucket)
×
298
                if err != nil {
299
                        return nil, err
640✔
300
                }
640✔
301
        }
1,026✔
302

386✔
303
        // Get failure reason if available.
386✔
304
        var failureReason *FailureReason
386✔
UNCOV
305
        b := bucket.Get(paymentFailInfoKey)
×
UNCOV
306
        if b != nil {
×
307
                reason := FailureReason(b[0])
308
                failureReason = &reason
309
        }
310

640✔
311
        // Create a new payment.
640✔
312
        payment := &MPPayment{
713✔
313
                SequenceNum:   sequenceNum,
73✔
314
                Info:          creationInfo,
73✔
315
                HTLCs:         htlcs,
73✔
316
                FailureReason: failureReason,
317
        }
318

640✔
319
        // Set its state and status.
640✔
320
        if err := payment.setState(); err != nil {
640✔
321
                return nil, err
640✔
322
        }
640✔
323

640✔
324
        return payment, nil
640✔
325
}
640✔
326

640✔
UNCOV
327
// fetchHtlcAttempts retrieves all htlc attempts made for the payment found in
×
UNCOV
328
// the given bucket.
×
329
func fetchHtlcAttempts(bucket kvdb.RBucket) ([]HTLCAttempt, error) {
330
        htlcsMap := make(map[uint64]*HTLCAttempt)
640✔
331

332
        attemptInfoCount := 0
333
        err := bucket.ForEach(func(k, v []byte) error {
334
                aid := byteOrder.Uint64(k[len(k)-8:])
335

393✔
336
                if _, ok := htlcsMap[aid]; !ok {
393✔
337
                        htlcsMap[aid] = &HTLCAttempt{}
393✔
338
                }
393✔
339

1,488✔
340
                var err error
1,095✔
341
                switch {
1,095✔
342
                case bytes.HasPrefix(k, htlcAttemptInfoKey):
1,796✔
343
                        attemptInfo, err := readHtlcAttemptInfo(v)
701✔
344
                        if err != nil {
701✔
345
                                return err
346
                        }
1,095✔
347

1,095✔
348
                        attemptInfo.AttemptID = aid
701✔
349
                        htlcsMap[aid].HTLCAttemptInfo = *attemptInfo
701✔
350
                        attemptInfoCount++
701✔
UNCOV
351

×
UNCOV
352
                case bytes.HasPrefix(k, htlcSettleInfoKey):
×
353
                        htlcsMap[aid].Settle, err = readHtlcSettleInfo(v)
354
                        if err != nil {
701✔
355
                                return err
701✔
356
                        }
701✔
357

358
                case bytes.HasPrefix(k, htlcFailInfoKey):
89✔
359
                        htlcsMap[aid].Failure, err = readHtlcFailInfo(v)
89✔
360
                        if err != nil {
89✔
361
                                return err
×
362
                        }
×
363

364
                default:
305✔
365
                        return fmt.Errorf("unknown htlc attempt key")
305✔
366
                }
305✔
UNCOV
367

×
UNCOV
368
                return nil
×
369
        })
UNCOV
370
        if err != nil {
×
371
                return nil, err
×
372
        }
373

374
        // Sanity check that all htlcs have an attempt info.
1,095✔
375
        if attemptInfoCount != len(htlcsMap) {
376
                return nil, errNoAttemptInfo
393✔
377
        }
×
UNCOV
378

×
379
        keys := make([]uint64, len(htlcsMap))
380
        i := 0
381
        for k := range htlcsMap {
393✔
UNCOV
382
                keys[i] = k
×
UNCOV
383
                i++
×
384
        }
385

393✔
386
        // Sort HTLC attempts by their attempt ID. This is needed because in the
393✔
387
        // DB we store the attempts with keys prefixed by their status which
1,094✔
388
        // changes order (groups them together by status).
701✔
389
        sort.Slice(keys, func(i, j int) bool {
701✔
390
                return keys[i] < keys[j]
701✔
391
        })
392

393
        htlcs := make([]HTLCAttempt, len(htlcsMap))
394
        for i, key := range keys {
395
                htlcs[i] = *htlcsMap[key]
738✔
396
        }
345✔
397

345✔
398
        return htlcs, nil
399
}
393✔
400

1,094✔
401
// readHtlcAttemptInfo reads the payment attempt info for this htlc.
701✔
402
func readHtlcAttemptInfo(b []byte) (*HTLCAttemptInfo, error) {
701✔
403
        r := bytes.NewReader(b)
404
        return deserializeHTLCAttemptInfo(r)
393✔
405
}
406

407
// readHtlcSettleInfo reads the settle info for the htlc. If the htlc isn't
408
// settled, nil is returned.
701✔
409
func readHtlcSettleInfo(b []byte) (*HTLCSettleInfo, error) {
701✔
410
        r := bytes.NewReader(b)
701✔
411
        return deserializeHTLCSettleInfo(r)
701✔
412
}
413

414
// readHtlcFailInfo reads the failure info for the htlc. If the htlc hasn't
415
// failed, nil is returned.
89✔
416
func readHtlcFailInfo(b []byte) (*HTLCFailInfo, error) {
89✔
417
        r := bytes.NewReader(b)
89✔
418
        return deserializeHTLCFailInfo(r)
89✔
419
}
420

421
// fetchFailedHtlcKeys retrieves the bucket keys of all failed HTLCs of a
422
// payment bucket.
305✔
423
func fetchFailedHtlcKeys(bucket kvdb.RBucket) ([][]byte, error) {
305✔
424
        htlcsBucket := bucket.NestedReadBucket(paymentHtlcsBucket)
305✔
425

305✔
426
        var htlcs []HTLCAttempt
427
        var err error
428
        if htlcsBucket != nil {
429
                htlcs, err = fetchHtlcAttempts(htlcsBucket)
7✔
430
                if err != nil {
7✔
431
                        return nil, err
7✔
432
                }
7✔
433
        }
7✔
434

14✔
435
        // Now iterate though them and save the bucket keys for the failed
7✔
436
        // HTLCs.
7✔
UNCOV
437
        var htlcKeys [][]byte
×
UNCOV
438
        for _, h := range htlcs {
×
439
                if h.Failure == nil {
440
                        continue
441
                }
442

443
                htlcKeyBytes := make([]byte, 8)
7✔
444
                binary.BigEndian.PutUint64(htlcKeyBytes, h.AttemptID)
19✔
445

15✔
446
                htlcKeys = append(htlcKeys, htlcKeyBytes)
3✔
447
        }
448

449
        return htlcKeys, nil
9✔
450
}
9✔
451

9✔
452
// PaymentsQuery represents a query to the payments database starting or ending
9✔
453
// at a certain offset index. The number of retrieved records can be limited.
454
type PaymentsQuery struct {
455
        // IndexOffset determines the starting point of the payments query and
7✔
456
        // is always exclusive. In normal order, the query starts at the next
457
        // higher (available) index compared to IndexOffset. In reversed order,
458
        // the query ends at the next lower (available) index compared to the
459
        // IndexOffset. In the case of a zero index_offset, the query will start
460
        // with the oldest payment when paginating forwards, or will end with
461
        // the most recent payment when paginating backwards.
462
        IndexOffset uint64
463

464
        // MaxPayments is the maximal number of payments returned in the
465
        // payments query.
466
        MaxPayments uint64
467

468
        // Reversed gives a meaning to the IndexOffset. If reversed is set to
469
        // true, the query will fetch payments with indices lower than the
470
        // IndexOffset, otherwise, it will return payments with indices greater
471
        // than the IndexOffset.
472
        Reversed bool
473

474
        // If IncludeIncomplete is true, then return payments that have not yet
475
        // fully completed. This means that pending payments, as well as failed
476
        // payments will show up if this field is set to true.
477
        IncludeIncomplete bool
478

479
        // CountTotal indicates that all payments currently present in the
480
        // payment index (complete and incomplete) should be counted.
481
        CountTotal bool
482

483
        // CreationDateStart, expressed in Unix seconds, if set, filters out
484
        // all payments with a creation date greater than or equal to it.
485
        CreationDateStart int64
486

487
        // CreationDateEnd, expressed in Unix seconds, if set, filters out all
488
        // payments with a creation date less than or equal to it.
489
        CreationDateEnd int64
490
}
491

492
// PaymentsResponse contains the result of a query to the payments database.
493
// It includes the set of payments that match the query and integers which
494
// represent the index of the first and last item returned in the series of
495
// payments. These integers allow callers to resume their query in the event
496
// that the query's response exceeds the max number of returnable events.
497
type PaymentsResponse struct {
498
        // Payments is the set of payments returned from the database for the
499
        // PaymentsQuery.
500
        Payments []*MPPayment
501

502
        // FirstIndexOffset is the index of the first element in the set of
503
        // returned MPPayments. Callers can use this to resume their query
504
        // in the event that the slice has too many events to fit into a single
505
        // response. The offset can be used to continue reverse pagination.
506
        FirstIndexOffset uint64
507

508
        // LastIndexOffset is the index of the last element in the set of
509
        // returned MPPayments. Callers can use this to resume their query
510
        // in the event that the slice has too many events to fit into a single
511
        // response. The offset can be used to continue forward pagination.
512
        LastIndexOffset uint64
513

514
        // TotalCount represents the total number of payments that are currently
515
        // stored in the payment database. This will only be set if the
516
        // CountTotal field in the query was set to true.
517
        TotalCount uint64
518
}
519

520
// QueryPayments is a query to the payments database which is restricted
521
// to a subset of payments by the payments query, containing an offset
522
// index and a maximum number of returned payments.
523
func (d *DB) QueryPayments(query PaymentsQuery) (PaymentsResponse, error) {
524
        var resp PaymentsResponse
525

526
        if err := kvdb.View(d, func(tx kvdb.RTx) error {
527
                // Get the root payments bucket.
528
                paymentsBucket := tx.ReadBucket(paymentsRootBucket)
529
                if paymentsBucket == nil {
36✔
530
                        return nil
36✔
531
                }
36✔
532

72✔
533
                // Get the index bucket which maps sequence number -> payment
36✔
534
                // hash and duplicate bool. If we have a payments bucket, we
36✔
535
                // should have an indexes bucket as well.
54✔
536
                indexes := tx.ReadBucket(paymentsIndexBucket)
18✔
537
                if indexes == nil {
18✔
538
                        return fmt.Errorf("index bucket does not exist")
539
                }
540

541
                // accumulatePayments gets payments with the sequence number
542
                // and hash provided and adds them to our list of payments if
18✔
543
                // they meet the criteria of our query. It returns the number
18✔
UNCOV
544
                // of payments that were added.
×
UNCOV
545
                accumulatePayments := func(sequenceKey, hash []byte) (bool,
×
546
                        error) {
547

548
                        r := bytes.NewReader(hash)
549
                        paymentHash, err := deserializePaymentIndex(r)
550
                        if err != nil {
551
                                return false, err
18✔
552
                        }
70✔
553

52✔
554
                        payment, err := fetchPaymentWithSequenceNumber(
52✔
555
                                tx, paymentHash, sequenceKey,
52✔
556
                        )
52✔
UNCOV
557
                        if err != nil {
×
558
                                return false, err
×
559
                        }
560

52✔
561
                        // To keep compatibility with the old API, we only
52✔
562
                        // return non-succeeded payments if requested.
52✔
563
                        if payment.Status != StatusSucceeded &&
52✔
UNCOV
564
                                !query.IncludeIncomplete {
×
UNCOV
565

×
566
                                return false, err
567
                        }
568

569
                        // Get the creation time in Unix seconds, this always
52✔
570
                        // rounds down the nanoseconds to full seconds.
57✔
571
                        createTime := payment.Info.CreationTime.Unix()
5✔
572

5✔
573
                        // Skip any payments that were created before the
5✔
574
                        // specified time.
575
                        if createTime < query.CreationDateStart {
576
                                return false, nil
577
                        }
47✔
578

47✔
579
                        // Skip any payments that were created after the
47✔
580
                        // specified time.
47✔
581
                        if query.CreationDateEnd != 0 &&
56✔
582
                                createTime > query.CreationDateEnd {
9✔
583

9✔
584
                                return false, nil
585
                        }
586

587
                        // At this point, we've exhausted the offset, so we'll
38✔
588
                        // begin collecting invoices found within the range.
40✔
589
                        resp.Payments = append(resp.Payments, payment)
2✔
590
                        return true, nil
2✔
591
                }
2✔
592

593
                // Create a paginator which reads from our sequence index bucket
594
                // with the parameters provided by the payments query.
595
                paginator := newPaginator(
36✔
596
                        indexes.ReadCursor(), query.Reversed, query.IndexOffset,
36✔
597
                        query.MaxPayments,
598
                )
599

600
                // Run a paginated query, adding payments to our response.
601
                if err := paginator.query(accumulatePayments); err != nil {
18✔
602
                        return err
18✔
603
                }
18✔
604

18✔
605
                // Counting the total number of payments is expensive, since we
18✔
606
                // literally have to traverse the cursor linearly, which can
18✔
607
                // take quite a while. So it's an optional query parameter.
18✔
UNCOV
608
                if query.CountTotal {
×
609
                        var (
×
610
                                totalPayments uint64
611
                                err           error
612
                        )
613
                        countFn := func(_, _ []byte) error {
614
                                totalPayments++
18✔
615

×
616
                                return nil
×
617
                        }
×
UNCOV
618

×
UNCOV
619
                        // In non-boltdb database backends, there's a faster
×
UNCOV
620
                        // ForAll query that allows for batch fetching items.
×
621
                        if fastBucket, ok := indexes.(kvdb.ExtendedRBucket); ok {
×
622
                                err = fastBucket.ForAll(countFn)
×
623
                        } else {
×
624
                                err = indexes.ForEach(countFn)
625
                        }
626
                        if err != nil {
627
                                return fmt.Errorf("error counting payments: %w",
×
628
                                        err)
×
629
                        }
×
UNCOV
630

×
631
                        resp.TotalCount = totalPayments
×
UNCOV
632
                }
×
UNCOV
633

×
UNCOV
634
                return nil
×
UNCOV
635
        }, func() {
×
636
                resp = PaymentsResponse{}
UNCOV
637
        }); err != nil {
×
638
                return resp, err
639
        }
640

18✔
641
        // Need to swap the payments slice order if reversed order.
36✔
642
        if query.Reversed {
36✔
643
                for l, r := 0, len(resp.Payments)-1; l < r; l, r = l+1, r-1 {
36✔
UNCOV
644
                        resp.Payments[l], resp.Payments[r] =
×
UNCOV
645
                                resp.Payments[r], resp.Payments[l]
×
646
                }
647
        }
648

52✔
649
        // Set the first and last index of the returned payments so that the
24✔
650
        // caller can resume from this point later on.
8✔
651
        if len(resp.Payments) > 0 {
8✔
652
                resp.FirstIndexOffset = resp.Payments[0].SequenceNum
8✔
653
                resp.LastIndexOffset =
654
                        resp.Payments[len(resp.Payments)-1].SequenceNum
655
        }
656

657
        return resp, nil
52✔
658
}
16✔
659

16✔
660
// fetchPaymentWithSequenceNumber get the payment which matches the payment hash
16✔
661
// *and* sequence number provided from the database. This is required because
16✔
662
// we previously had more than one payment per hash, so we have multiple indexes
663
// pointing to a single payment; we want to retrieve the correct one.
36✔
664
func fetchPaymentWithSequenceNumber(tx kvdb.RTx, paymentHash lntypes.Hash,
665
        sequenceNumber []byte) (*MPPayment, error) {
666

667
        // We can now lookup the payment keyed by its hash in
668
        // the payments root bucket.
669
        bucket, err := fetchPaymentBucket(tx, paymentHash)
670
        if err != nil {
671
                return nil, err
58✔
672
        }
58✔
673

58✔
674
        // A single payment hash can have multiple payments associated with it.
58✔
675
        // We lookup our sequence number first, to determine whether this is
58✔
676
        // the payment we are actually looking for.
58✔
UNCOV
677
        seqBytes := bucket.Get(paymentSequenceKey)
×
UNCOV
678
        if seqBytes == nil {
×
679
                return nil, ErrNoSequenceNumber
680
        }
681

682
        // If this top level payment has the sequence number we are looking for,
683
        // return it.
58✔
684
        if bytes.Equal(seqBytes, sequenceNumber) {
58✔
UNCOV
685
                return fetchPayment(bucket)
×
UNCOV
686
        }
×
687

688
        // If we were not looking for the top level payment, we are looking for
689
        // one of our duplicate payments. We need to iterate through the seq
690
        // numbers in this bucket to find the correct payments. If we do not
103✔
691
        // find a duplicate payments bucket here, something is wrong.
45✔
692
        dup := bucket.NestedReadBucket(duplicatePaymentsBucket)
45✔
693
        if dup == nil {
694
                return nil, ErrNoDuplicateBucket
695
        }
696

697
        var duplicatePayment *MPPayment
698
        err = dup.ForEach(func(k, v []byte) error {
13✔
699
                subBucket := dup.NestedReadBucket(k)
14✔
700
                if subBucket == nil {
1✔
701
                        // We one bucket for each duplicate to be found.
1✔
702
                        return ErrNoDuplicateNestedBucket
703
                }
12✔
704

27✔
705
                seqBytes := subBucket.Get(duplicatePaymentSequenceKey)
15✔
706
                if seqBytes == nil {
15✔
707
                        return err
×
708
                }
×
UNCOV
709

×
710
                // If this duplicate payment is not the sequence number we are
711
                // looking for, we can continue.
15✔
712
                if !bytes.Equal(seqBytes, sequenceNumber) {
15✔
UNCOV
713
                        return nil
×
UNCOV
714
                }
×
715

716
                duplicatePayment, err = fetchDuplicatePayment(subBucket)
717
                if err != nil {
718
                        return err
19✔
719
                }
4✔
720

4✔
721
                return nil
722
        })
11✔
723
        if err != nil {
11✔
724
                return nil, err
×
725
        }
×
726

727
        // If none of the duplicate payments matched our sequence number, we
11✔
728
        // failed to find the payment with this sequence number; something is
729
        // wrong.
12✔
UNCOV
730
        if duplicatePayment == nil {
×
UNCOV
731
                return nil, ErrDuplicateNotFound
×
732
        }
733

734
        return duplicatePayment, nil
735
}
736

13✔
737
// DeletePayment deletes a payment from the DB given its payment hash. If
1✔
738
// failedHtlcsOnly is set, only failed HTLC attempts of the payment will be
1✔
739
// deleted.
740
func (d *DB) DeletePayment(paymentHash lntypes.Hash,
11✔
741
        failedHtlcsOnly bool) error {
742

743
        return kvdb.Update(d, func(tx kvdb.RwTx) error {
744
                payments := tx.ReadWriteBucket(paymentsRootBucket)
745
                if payments == nil {
746
                        return nil
747
                }
11✔
748

11✔
749
                bucket := payments.NestedReadWriteBucket(paymentHash[:])
22✔
750
                if bucket == nil {
11✔
751
                        return fmt.Errorf("non bucket element in payments " +
11✔
UNCOV
752
                                "bucket")
×
UNCOV
753
                }
×
754

755
                // If the status is InFlight, we cannot safely delete
11✔
756
                // the payment information, so we return early.
12✔
757
                paymentStatus, err := fetchPaymentStatus(bucket)
1✔
758
                if err != nil {
1✔
759
                        return err
1✔
760
                }
761

762
                // If the payment has inflight HTLCs, we cannot safely delete
763
                // the payment information, so we return an error.
10✔
764
                if err := paymentStatus.removable(); err != nil {
10✔
UNCOV
765
                        return fmt.Errorf("payment '%v' has inflight HTLCs"+
×
UNCOV
766
                                "and therefore cannot be deleted: %w",
×
767
                                paymentHash.String(), err)
768
                }
769

770
                // Delete the failed HTLC attempts we found.
13✔
771
                if failedHtlcsOnly {
3✔
772
                        toDelete, err := fetchFailedHtlcKeys(bucket)
3✔
773
                        if err != nil {
3✔
774
                                return err
3✔
775
                        }
776

777
                        htlcsBucket := bucket.NestedReadWriteBucket(
11✔
778
                                paymentHtlcsBucket,
4✔
779
                        )
4✔
UNCOV
780

×
UNCOV
781
                        for _, htlcID := range toDelete {
×
782
                                err = htlcsBucket.Delete(
783
                                        htlcBucketKey(htlcAttemptInfoKey, htlcID),
4✔
784
                                )
4✔
785
                                if err != nil {
4✔
786
                                        return err
4✔
787
                                }
10✔
788

6✔
789
                                err = htlcsBucket.Delete(
6✔
790
                                        htlcBucketKey(htlcFailInfoKey, htlcID),
6✔
791
                                )
6✔
UNCOV
792
                                if err != nil {
×
793
                                        return err
×
794
                                }
795

6✔
796
                                err = htlcsBucket.Delete(
6✔
797
                                        htlcBucketKey(htlcSettleInfoKey, htlcID),
6✔
798
                                )
6✔
UNCOV
799
                                if err != nil {
×
800
                                        return err
×
801
                                }
802
                        }
6✔
803

6✔
804
                        return nil
6✔
805
                }
6✔
UNCOV
806

×
UNCOV
807
                seqNrs, err := fetchSequenceNumbers(bucket)
×
808
                if err != nil {
809
                        return err
810
                }
4✔
811

812
                if err := payments.DeleteNestedBucket(paymentHash[:]); err != nil {
813
                        return err
3✔
814
                }
3✔
UNCOV
815

×
UNCOV
816
                indexBucket := tx.ReadWriteBucket(paymentsIndexBucket)
×
817
                for _, k := range seqNrs {
818
                        if err := indexBucket.Delete(k); err != nil {
3✔
819
                                return err
×
820
                        }
×
821
                }
822

3✔
823
                return nil
6✔
824
        }, func() {})
3✔
UNCOV
825
}
×
UNCOV
826

×
827
// DeletePayments deletes all completed and failed payments from the DB. If
828
// failedOnly is set, only failed payments will be considered for deletion. If
829
// failedHtlcsOnly is set, the payment itself won't be deleted, only failed HTLC
3✔
830
// attempts. The method returns the number of deleted payments, which is always
11✔
831
// 0 if failedHtlcsOnly is set.
832
func (d *DB) DeletePayments(failedOnly, failedHtlcsOnly bool) (int, error) {
833
        var numPayments int
834
        err := kvdb.Update(d, func(tx kvdb.RwTx) error {
835
                payments := tx.ReadWriteBucket(paymentsRootBucket)
836
                if payments == nil {
837
                        return nil
838
                }
6✔
839

6✔
840
                var (
12✔
841
                        // deleteBuckets is the set of payment buckets we need
6✔
842
                        // to delete.
6✔
UNCOV
843
                        deleteBuckets [][]byte
×
UNCOV
844

×
845
                        // deleteIndexes is the set of indexes pointing to these
846
                        // payments that need to be deleted.
6✔
847
                        deleteIndexes [][]byte
6✔
848

6✔
849
                        // deleteHtlcs maps a payment hash to the HTLC IDs we
6✔
850
                        // want to delete for that payment.
6✔
851
                        deleteHtlcs = make(map[lntypes.Hash][][]byte)
6✔
852
                )
6✔
853
                err := payments.ForEach(func(k, _ []byte) error {
6✔
854
                        bucket := payments.NestedReadBucket(k)
6✔
855
                        if bucket == nil {
6✔
856
                                // We only expect sub-buckets to be found in
6✔
857
                                // this top-level bucket.
6✔
858
                                return fmt.Errorf("non bucket element in " +
6✔
859
                                        "payments bucket")
24✔
860
                        }
18✔
861

18✔
UNCOV
862
                        // If the status is InFlight, we cannot safely delete
×
UNCOV
863
                        // the payment information, so we return early.
×
UNCOV
864
                        paymentStatus, err := fetchPaymentStatus(bucket)
×
UNCOV
865
                        if err != nil {
×
866
                                return err
×
867
                        }
868

869
                        // If the payment has inflight HTLCs, we cannot safely
870
                        // delete the payment information, so we return an nil
18✔
871
                        // to skip it.
18✔
UNCOV
872
                        if err := paymentStatus.removable(); err != nil {
×
UNCOV
873
                                return nil
×
874
                        }
875

876
                        // If we requested to only delete failed payments, we
877
                        // can return if this one is not.
878
                        if failedOnly && paymentStatus != StatusFailed {
24✔
879
                                return nil
6✔
880
                        }
6✔
881

882
                        // If we are only deleting failed HTLCs, fetch them.
883
                        if failedHtlcsOnly {
884
                                toDelete, err := fetchFailedHtlcKeys(bucket)
16✔
885
                                if err != nil {
4✔
886
                                        return err
4✔
887
                                }
888

889
                                hash, err := lntypes.MakeHash(k)
11✔
890
                                if err != nil {
3✔
891
                                        return err
3✔
892
                                }
×
UNCOV
893

×
894
                                deleteHtlcs[hash] = toDelete
895

3✔
896
                                // We return, we are only deleting attempts.
3✔
UNCOV
897
                                return nil
×
UNCOV
898
                        }
×
899

900
                        // Add the bucket to the set of buckets we can delete.
3✔
901
                        deleteBuckets = append(deleteBuckets, k)
3✔
902

3✔
903
                        // Get all the sequence number associated with the
3✔
904
                        // payment, including duplicates.
905
                        seqNrs, err := fetchSequenceNumbers(bucket)
906
                        if err != nil {
907
                                return err
5✔
908
                        }
5✔
909

5✔
910
                        deleteIndexes = append(deleteIndexes, seqNrs...)
5✔
911
                        numPayments++
5✔
912
                        return nil
5✔
UNCOV
913
                })
×
UNCOV
914
                if err != nil {
×
915
                        return err
916
                }
5✔
917

5✔
918
                // Delete the failed HTLC attempts we found.
5✔
919
                for hash, htlcIDs := range deleteHtlcs {
920
                        bucket := payments.NestedReadWriteBucket(hash[:])
6✔
UNCOV
921
                        htlcsBucket := bucket.NestedReadWriteBucket(
×
UNCOV
922
                                paymentHtlcsBucket,
×
923
                        )
924

925
                        for _, aid := range htlcIDs {
9✔
926
                                if err := htlcsBucket.Delete(
3✔
927
                                        htlcBucketKey(htlcAttemptInfoKey, aid),
3✔
928
                                ); err != nil {
3✔
929
                                        return err
3✔
930
                                }
3✔
931

6✔
932
                                if err := htlcsBucket.Delete(
3✔
933
                                        htlcBucketKey(htlcFailInfoKey, aid),
3✔
934
                                ); err != nil {
3✔
935
                                        return err
×
936
                                }
×
937

938
                                if err := htlcsBucket.Delete(
3✔
939
                                        htlcBucketKey(htlcSettleInfoKey, aid),
3✔
940
                                ); err != nil {
3✔
941
                                        return err
×
942
                                }
×
943
                        }
944
                }
3✔
945

3✔
946
                for _, k := range deleteBuckets {
3✔
UNCOV
947
                        if err := payments.DeleteNestedBucket(k); err != nil {
×
948
                                return err
×
949
                        }
950
                }
951

952
                // Get our index bucket and delete all indexes pointing to the
11✔
953
                // payments we are deleting.
5✔
UNCOV
954
                indexBucket := tx.ReadWriteBucket(paymentsIndexBucket)
×
UNCOV
955
                for _, k := range deleteIndexes {
×
956
                        if err := indexBucket.Delete(k); err != nil {
957
                                return err
958
                        }
959
                }
960

6✔
961
                return nil
12✔
962
        }, func() {
6✔
UNCOV
963
                numPayments = 0
×
UNCOV
964
        })
×
965
        if err != nil {
966
                return 0, err
967
        }
6✔
968

6✔
969
        return numPayments, nil
6✔
970
}
6✔
971

6✔
UNCOV
972
// fetchSequenceNumbers fetches all the sequence numbers associated with a
×
UNCOV
973
// payment, including those belonging to any duplicate payments.
×
974
func fetchSequenceNumbers(paymentBucket kvdb.RBucket) ([][]byte, error) {
975
        seqNum := paymentBucket.Get(paymentSequenceKey)
6✔
976
        if seqNum == nil {
977
                return nil, errors.New("expected sequence number")
978
        }
979

980
        sequenceNumbers := [][]byte{seqNum}
8✔
981

8✔
982
        // Get the duplicate payments bucket, if it has no duplicates, just
8✔
UNCOV
983
        // return early with the payment sequence number.
×
UNCOV
984
        duplicates := paymentBucket.NestedReadBucket(duplicatePaymentsBucket)
×
985
        if duplicates == nil {
986
                return sequenceNumbers, nil
8✔
987
        }
8✔
988

8✔
989
        // If we do have duplicated, they are keyed by sequence number, so we
8✔
990
        // iterate through the duplicates bucket and add them to our set of
8✔
991
        // sequence numbers.
15✔
992
        if err := duplicates.ForEach(func(k, v []byte) error {
7✔
993
                sequenceNumbers = append(sequenceNumbers, k)
7✔
994
                return nil
995
        }); err != nil {
996
                return nil, err
997
        }
998

2✔
999
        return sequenceNumbers, nil
1✔
1000
}
1✔
1001

1✔
UNCOV
1002
// nolint: dupl
×
UNCOV
1003
func serializePaymentCreationInfo(w io.Writer, c *PaymentCreationInfo) error {
×
1004
        var scratch [8]byte
1005

1✔
1006
        if _, err := w.Write(c.PaymentIdentifier[:]); err != nil {
1007
                return err
1008
        }
1009

151✔
1010
        byteOrder.PutUint64(scratch[:], uint64(c.Value))
151✔
1011
        if _, err := w.Write(scratch[:]); err != nil {
151✔
1012
                return err
151✔
1013
        }
×
UNCOV
1014

×
1015
        if err := serializeTime(w, c.CreationTime); err != nil {
1016
                return err
151✔
1017
        }
151✔
UNCOV
1018

×
UNCOV
1019
        byteOrder.PutUint32(scratch[:4], uint32(len(c.PaymentRequest)))
×
1020
        if _, err := w.Write(scratch[:4]); err != nil {
1021
                return err
151✔
1022
        }
×
UNCOV
1023

×
1024
        if _, err := w.Write(c.PaymentRequest[:]); err != nil {
1025
                return err
151✔
1026
        }
151✔
UNCOV
1027

×
UNCOV
1028
        // Any remaining bytes are TLV encoded records. Currently, these are
×
1029
        // only the custom records provided by the user to be sent to the first
1030
        // hop. But this can easily be extended with further records by merging
151✔
UNCOV
1031
        // the records into a single TLV stream.
×
UNCOV
1032
        err := c.FirstHopCustomRecords.SerializeTo(w)
×
1033
        if err != nil {
1034
                return err
1035
        }
1036

1037
        return nil
1038
}
151✔
1039

151✔
UNCOV
1040
func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo,
×
UNCOV
1041
        error) {
×
1042

1043
        var scratch [8]byte
151✔
1044

1045
        c := &PaymentCreationInfo{}
1046

1047
        if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil {
642✔
1048
                return nil, err
642✔
1049
        }
642✔
1050

642✔
1051
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
642✔
1052
                return nil, err
642✔
1053
        }
642✔
UNCOV
1054
        c.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
×
UNCOV
1055

×
1056
        creationTime, err := deserializeTime(r)
1057
        if err != nil {
642✔
1058
                return nil, err
×
1059
        }
×
1060
        c.CreationTime = creationTime
642✔
1061

642✔
1062
        if _, err := io.ReadFull(r, scratch[:4]); err != nil {
642✔
1063
                return nil, err
642✔
1064
        }
×
UNCOV
1065

×
1066
        reqLen := uint32(byteOrder.Uint32(scratch[:4]))
642✔
1067
        payReq := make([]byte, reqLen)
642✔
1068
        if reqLen > 0 {
642✔
UNCOV
1069
                if _, err := io.ReadFull(r, payReq); err != nil {
×
1070
                        return nil, err
×
1071
                }
1072
        }
642✔
1073
        c.PaymentRequest = payReq
642✔
1074

1,284✔
1075
        // Any remaining bytes are TLV encoded records. Currently, these are
642✔
UNCOV
1076
        // only the custom records provided by the user to be sent to the first
×
UNCOV
1077
        // hop. But this can easily be extended with further records by merging
×
1078
        // the records into a single TLV stream.
1079
        c.FirstHopCustomRecords, err = lnwire.ParseCustomRecordsFrom(r)
642✔
1080
        if err != nil {
642✔
1081
                return nil, err
642✔
1082
        }
642✔
1083

642✔
1084
        return c, nil
642✔
1085
}
642✔
1086

642✔
UNCOV
1087
func serializeHTLCAttemptInfo(w io.Writer, a *HTLCAttemptInfo) error {
×
UNCOV
1088
        if err := WriteElements(w, a.sessionKey); err != nil {
×
1089
                return err
1090
        }
642✔
1091

1092
        if err := SerializeRoute(w, a.Route); err != nil {
1093
                return err
72✔
1094
        }
72✔
UNCOV
1095

×
UNCOV
1096
        if err := serializeTime(w, a.AttemptTime); err != nil {
×
1097
                return err
1098
        }
72✔
UNCOV
1099

×
UNCOV
1100
        // If the hash is nil we can just return.
×
1101
        if a.Hash == nil {
1102
                return nil
72✔
1103
        }
×
UNCOV
1104

×
1105
        if _, err := w.Write(a.Hash[:]); err != nil {
1106
                return err
1107
        }
72✔
UNCOV
1108

×
UNCOV
1109
        // Merge the fixed/known records together with the custom records to
×
1110
        // serialize them as a single blob. We can't do this in SerializeRoute
1111
        // because we're in the middle of the byte stream there. We can only do
72✔
UNCOV
1112
        // TLV serialization at the end of the stream, since EOF is allowed for
×
UNCOV
1113
        // a stream if no more data is expected.
×
1114
        producers := []tlv.RecordProducer{
1115
                &a.Route.FirstHopAmount,
1116
        }
1117
        tlvData, err := lnwire.MergeAndEncode(
1118
                producers, nil, a.Route.FirstHopWireCustomRecords,
1119
        )
1120
        if err != nil {
72✔
1121
                return err
72✔
1122
        }
72✔
1123

72✔
1124
        if _, err := w.Write(tlvData); err != nil {
72✔
1125
                return err
72✔
1126
        }
72✔
UNCOV
1127

×
UNCOV
1128
        return nil
×
1129
}
1130

72✔
UNCOV
1131
func deserializeHTLCAttemptInfo(r io.Reader) (*HTLCAttemptInfo, error) {
×
UNCOV
1132
        a := &HTLCAttemptInfo{}
×
1133
        err := ReadElements(r, &a.sessionKey)
1134
        if err != nil {
72✔
1135
                return nil, err
1136
        }
1137

703✔
1138
        a.Route, err = DeserializeRoute(r)
703✔
1139
        if err != nil {
703✔
1140
                return nil, err
703✔
1141
        }
×
UNCOV
1142

×
1143
        a.AttemptTime, err = deserializeTime(r)
1144
        if err != nil {
703✔
1145
                return nil, err
703✔
1146
        }
×
UNCOV
1147

×
1148
        hash := lntypes.Hash{}
1149
        _, err = io.ReadFull(r, hash[:])
703✔
1150

703✔
UNCOV
1151
        switch {
×
UNCOV
1152
        // Older payment attempts wouldn't have the hash set, in which case we
×
1153
        // can just return.
1154
        case err == io.EOF, err == io.ErrUnexpectedEOF:
703✔
1155
                return a, nil
703✔
1156

703✔
1157
        case err != nil:
703✔
1158
                return nil, err
1159

UNCOV
1160
        default:
×
UNCOV
1161
        }
×
1162

UNCOV
1163
        a.Hash = &hash
×
UNCOV
1164

×
1165
        // Read any remaining data (if any) and parse it into the known records
1166
        // and custom records.
703✔
1167
        extraData, err := io.ReadAll(r)
1168
        if err != nil {
1169
                return nil, err
703✔
1170
        }
703✔
1171

703✔
1172
        customRecords, _, _, err := lnwire.ParseAndExtractCustomRecords(
703✔
1173
                extraData, &a.Route.FirstHopAmount,
703✔
1174
        )
703✔
UNCOV
1175
        if err != nil {
×
1176
                return nil, err
×
1177
        }
1178

703✔
1179
        a.Route.FirstHopWireCustomRecords = customRecords
703✔
1180

703✔
1181
        return a, nil
703✔
UNCOV
1182
}
×
UNCOV
1183

×
1184
func serializeHop(w io.Writer, h *route.Hop) error {
1185
        if err := WriteElements(w,
703✔
1186
                h.PubKeyBytes[:],
703✔
1187
                h.ChannelID,
703✔
1188
                h.OutgoingTimeLock,
1189
                h.AmtToForward,
1190
        ); err != nil {
149✔
1191
                return err
149✔
1192
        }
149✔
1193

149✔
1194
        if err := binary.Write(w, byteOrder, h.LegacyPayload); err != nil {
149✔
1195
                return err
149✔
1196
        }
149✔
UNCOV
1197

×
UNCOV
1198
        // For legacy payloads, we don't need to write any TLV records, so
×
1199
        // we'll write a zero indicating the our serialized TLV map has no
1200
        // records.
149✔
UNCOV
1201
        if h.LegacyPayload {
×
UNCOV
1202
                return WriteElements(w, uint32(0))
×
1203
        }
1204

1205
        // Gather all non-primitive TLV records so that they can be serialized
1206
        // as a single blob.
1207
        //
229✔
1208
        // TODO(conner): add migration to unify all fields in a single TLV
80✔
1209
        // blobs. The split approach will cause headaches down the road as more
80✔
1210
        // fields are added, which we can avoid by having a single TLV stream
1211
        // for all payload fields.
1212
        var records []tlv.Record
1213
        if h.MPP != nil {
1214
                records = append(records, h.MPP.Record())
1215
        }
1216

1217
        // Add blinding point and encrypted data if present.
1218
        if h.EncryptedData != nil {
69✔
1219
                records = append(records, record.NewEncryptedDataRecord(
133✔
1220
                        &h.EncryptedData,
64✔
1221
                ))
64✔
1222
        }
1223

1224
        if h.BlindingPoint != nil {
71✔
1225
                records = append(records, record.NewBlindingPointRecord(
2✔
1226
                        &h.BlindingPoint,
2✔
1227
                ))
2✔
1228
        }
2✔
1229

1230
        if h.AMP != nil {
70✔
1231
                records = append(records, h.AMP.Record())
1✔
1232
        }
1✔
1233

1✔
1234
        if h.Metadata != nil {
1✔
1235
                records = append(records, record.NewMetadataRecord(&h.Metadata))
1236
        }
69✔
UNCOV
1237

×
UNCOV
1238
        if h.TotalAmtMsat != 0 {
×
1239
                totalMsatInt := uint64(h.TotalAmtMsat)
1240
                records = append(
136✔
1241
                        records, record.NewTotalAmtMsatBlinded(&totalMsatInt),
67✔
1242
                )
67✔
1243
        }
1244

70✔
1245
        // Final sanity check to absolutely rule out custom records that are not
1✔
1246
        // custom and write into the standard range.
1✔
1247
        if err := h.CustomRecords.Validate(); err != nil {
1✔
1248
                return err
1✔
1249
        }
1✔
1250

1251
        // Convert custom records to tlv and add to the record list.
1252
        // MapToRecords sorts the list, so adding it here will keep the list
1253
        // canonical.
69✔
UNCOV
1254
        tlvRecords := tlv.MapToRecords(h.CustomRecords)
×
UNCOV
1255
        records = append(records, tlvRecords...)
×
1256

1257
        // Otherwise, we'll transform our slice of records into a map of the
1258
        // raw bytes, then serialize them in-line with a length (number of
1259
        // elements) prefix.
1260
        mapRecords, err := tlv.RecordsToMap(records)
69✔
1261
        if err != nil {
69✔
1262
                return err
69✔
1263
        }
69✔
1264

69✔
1265
        numRecords := uint32(len(mapRecords))
69✔
1266
        if err := WriteElements(w, numRecords); err != nil {
69✔
1267
                return err
69✔
1268
        }
×
UNCOV
1269

×
1270
        for recordType, rawBytes := range mapRecords {
1271
                if err := WriteElements(w, recordType); err != nil {
69✔
1272
                        return err
69✔
1273
                }
×
UNCOV
1274

×
1275
                if err := wire.WriteVarBytes(w, 0, rawBytes); err != nil {
1276
                        return err
336✔
1277
                }
267✔
UNCOV
1278
        }
×
UNCOV
1279

×
1280
        return nil
1281
}
267✔
UNCOV
1282

×
UNCOV
1283
// maxOnionPayloadSize is the largest Sphinx payload possible, so we don't need
×
1284
// to read/write a TLV stream larger than this.
1285
const maxOnionPayloadSize = 1300
1286

69✔
1287
func deserializeHop(r io.Reader) (*route.Hop, error) {
1288
        h := &route.Hop{}
1289

1290
        var pub []byte
1291
        if err := ReadElements(r, &pub); err != nil {
1292
                return nil, err
1293
        }
1,411✔
1294
        copy(h.PubKeyBytes[:], pub)
1,411✔
1295

1,411✔
1296
        if err := ReadElements(r,
1,411✔
1297
                &h.ChannelID, &h.OutgoingTimeLock, &h.AmtToForward,
1,411✔
UNCOV
1298
        ); err != nil {
×
1299
                return nil, err
×
1300
        }
1,411✔
1301

1,411✔
1302
        // TODO(roasbeef): change field to allow LegacyPayload false to be the
1,411✔
1303
        // legacy default?
1,411✔
1304
        err := binary.Read(r, byteOrder, &h.LegacyPayload)
1,411✔
UNCOV
1305
        if err != nil {
×
1306
                return nil, err
×
1307
        }
1308

1309
        var numElements uint32
1310
        if err := ReadElements(r, &numElements); err != nil {
1,411✔
1311
                return nil, err
1,411✔
1312
        }
×
UNCOV
1313

×
1314
        // If there're no elements, then we can return early.
1315
        if numElements == 0 {
1,411✔
1316
                return h, nil
1,411✔
UNCOV
1317
        }
×
UNCOV
1318

×
1319
        tlvMap := make(map[uint64][]byte)
1320
        for i := uint32(0); i < numElements; i++ {
1321
                var tlvType uint64
2,143✔
1322
                if err := ReadElements(r, &tlvType); err != nil {
732✔
1323
                        return nil, err
732✔
1324
                }
1325

679✔
1326
                rawRecordBytes, err := wire.ReadVarBytes(
3,385✔
1327
                        r, 0, maxOnionPayloadSize, "tlv",
2,706✔
1328
                )
2,706✔
UNCOV
1329
                if err != nil {
×
1330
                        return nil, err
×
1331
                }
1332

2,706✔
1333
                tlvMap[tlvType] = rawRecordBytes
2,706✔
1334
        }
2,706✔
1335

2,706✔
UNCOV
1336
        // If the MPP type is present, remove it from the generic TLV map and
×
UNCOV
1337
        // parse it back into a proper MPP struct.
×
1338
        //
1339
        // TODO(conner): add migration to unify all fields in a single TLV
2,706✔
1340
        // blobs. The split approach will cause headaches down the road as more
1341
        // fields are added, which we can avoid by having a single TLV stream
1342
        // for all payload fields.
1343
        mppType := uint64(record.MPPOnionType)
1344
        if mppBytes, ok := tlvMap[mppType]; ok {
1345
                delete(tlvMap, mppType)
1346

1347
                var (
1348
                        mpp    = &record.MPP{}
1349
                        mppRec = mpp.Record()
679✔
1350
                        r      = bytes.NewReader(mppBytes)
1,352✔
1351
                )
673✔
1352
                err := mppRec.Decode(r, uint64(len(mppBytes)))
673✔
1353
                if err != nil {
673✔
1354
                        return nil, err
673✔
1355
                }
673✔
1356
                h.MPP = mpp
673✔
1357
        }
673✔
1358

673✔
1359
        // If encrypted data or blinding key are present, remove them from
673✔
UNCOV
1360
        // the TLV map and parse into proper types.
×
UNCOV
1361
        encryptedDataType := uint64(record.EncryptedDataOnionType)
×
1362
        if data, ok := tlvMap[encryptedDataType]; ok {
673✔
1363
                delete(tlvMap, encryptedDataType)
1364
                h.EncryptedData = data
1365
        }
1366

1367
        blindingType := uint64(record.BlindingPointOnionType)
679✔
1368
        if blindingPoint, ok := tlvMap[blindingType]; ok {
681✔
1369
                delete(tlvMap, blindingType)
2✔
1370

2✔
1371
                h.BlindingPoint, err = btcec.ParsePubKey(blindingPoint)
2✔
1372
                if err != nil {
1373
                        return nil, fmt.Errorf("invalid blinding point: %w",
679✔
1374
                                err)
680✔
1375
                }
1✔
1376
        }
1✔
1377

1✔
1378
        ampType := uint64(record.AMPOnionType)
1✔
UNCOV
1379
        if ampBytes, ok := tlvMap[ampType]; ok {
×
UNCOV
1380
                delete(tlvMap, ampType)
×
UNCOV
1381

×
1382
                var (
1383
                        amp    = &record.AMP{}
1384
                        ampRec = amp.Record()
679✔
1385
                        r      = bytes.NewReader(ampBytes)
679✔
UNCOV
1386
                )
×
UNCOV
1387
                err := ampRec.Decode(r, uint64(len(ampBytes)))
×
UNCOV
1388
                if err != nil {
×
1389
                        return nil, err
×
1390
                }
×
UNCOV
1391
                h.AMP = amp
×
UNCOV
1392
        }
×
UNCOV
1393

×
UNCOV
1394
        // If the metadata type is present, remove it from the tlv map and
×
UNCOV
1395
        // populate directly on the hop.
×
UNCOV
1396
        metadataType := uint64(record.MetadataOnionType)
×
UNCOV
1397
        if metadata, ok := tlvMap[metadataType]; ok {
×
1398
                delete(tlvMap, metadataType)
1399

1400
                h.Metadata = metadata
1401
        }
1402

679✔
1403
        totalAmtMsatType := uint64(record.TotalAmtMsatBlindedType)
1,356✔
1404
        if totalAmtMsat, ok := tlvMap[totalAmtMsatType]; ok {
677✔
1405
                delete(tlvMap, totalAmtMsatType)
677✔
1406

677✔
1407
                var (
677✔
1408
                        totalAmtMsatInt uint64
1409
                        buf             [8]byte
679✔
1410
                )
680✔
1411
                if err := tlv.DTUint64(
1✔
1412
                        bytes.NewReader(totalAmtMsat),
1✔
1413
                        &totalAmtMsatInt,
1✔
1414
                        &buf,
1✔
1415
                        uint64(len(totalAmtMsat)),
1✔
1416
                ); err != nil {
1✔
1417
                        return nil, err
1✔
1418
                }
1✔
1419

1✔
1420
                h.TotalAmtMsat = lnwire.MilliSatoshi(totalAmtMsatInt)
1✔
1421
        }
1✔
1422

1✔
UNCOV
1423
        h.CustomRecords = tlvMap
×
UNCOV
1424

×
1425
        return h, nil
1426
}
1✔
1427

1428
// SerializeRoute serializes a route.
1429
func SerializeRoute(w io.Writer, r route.Route) error {
679✔
1430
        if err := WriteElements(w,
679✔
1431
                r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
679✔
1432
        ); err != nil {
1433
                return err
1434
        }
1435

74✔
1436
        if err := WriteElements(w, uint32(len(r.Hops))); err != nil {
74✔
1437
                return err
74✔
1438
        }
74✔
UNCOV
1439

×
UNCOV
1440
        for _, h := range r.Hops {
×
1441
                if err := serializeHop(w, h); err != nil {
1442
                        return err
74✔
1443
                }
×
UNCOV
1444
        }
×
1445

1446
        // Any new/extra TLV data is encoded in serializeHTLCAttemptInfo!
223✔
1447

149✔
UNCOV
1448
        return nil
×
UNCOV
1449
}
×
1450

1451
// DeserializeRoute deserializes a route.
1452
func DeserializeRoute(r io.Reader) (route.Route, error) {
1453
        rt := route.Route{}
1454
        if err := ReadElements(r,
74✔
1455
                &rt.TotalTimeLock, &rt.TotalAmount,
1456
        ); err != nil {
1457
                return rt, err
1458
        }
705✔
1459

705✔
1460
        var pub []byte
705✔
1461
        if err := ReadElements(r, &pub); err != nil {
705✔
1462
                return rt, err
705✔
1463
        }
×
UNCOV
1464
        copy(rt.SourcePubKey[:], pub)
×
1465

1466
        var numHops uint32
705✔
1467
        if err := ReadElements(r, &numHops); err != nil {
705✔
1468
                return rt, err
×
1469
        }
×
1470

705✔
1471
        var hops []*route.Hop
705✔
1472
        for i := uint32(0); i < numHops; i++ {
705✔
1473
                hop, err := deserializeHop(r)
705✔
UNCOV
1474
                if err != nil {
×
1475
                        return rt, err
×
1476
                }
1477
                hops = append(hops, hop)
705✔
1478
        }
2,116✔
1479
        rt.Hops = hops
1,411✔
1480

1,411✔
UNCOV
1481
        // Any new/extra TLV data is decoded in deserializeHTLCAttemptInfo!
×
UNCOV
1482

×
1483
        return rt, nil
1,411✔
1484
}
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