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

lightningnetwork / lnd / 15100227105

18 May 2025 09:28PM UTC coverage: 68.997% (+0.007%) from 68.99%
15100227105

Pull #9822

github

web-flow
Merge 40492099f into 3707b1fb7
Pull Request #9822: Refactor Payments Code (1|?)

414 of 501 new or added lines in 16 files covered. (82.63%)

49 existing lines in 19 files now uncovered.

133955 of 194145 relevant lines covered (69.0%)

22105.67 hits per line

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

73.12
/channeldb/payments.go
1
package channeldb
2

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

11
        "github.com/btcsuite/btcd/btcec/v2"
12
        "github.com/btcsuite/btcd/wire"
13
        "github.com/lightningnetwork/lnd/kvdb"
14
        "github.com/lightningnetwork/lnd/lntypes"
15
        "github.com/lightningnetwork/lnd/lnwire"
16
        pymtpkg "github.com/lightningnetwork/lnd/payments"
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
// htlcBucketKey creates a composite key from prefix and id where the result is
107
// simply the two concatenated.
108
func htlcBucketKey(prefix, id []byte) []byte {
268✔
109
        key := make([]byte, len(prefix)+len(id))
268✔
110
        copy(key, prefix)
268✔
111
        copy(key[len(prefix):], id)
268✔
112
        return key
268✔
113
}
268✔
114

115
// FetchPayments returns all sent payments found in the DB.
116
//
117
// nolint: dupl
118
//
119
// TODO(replace with QueryPayments)
120
func (d *DB) FetchPayments() ([]*pymtpkg.MPPayment, error) {
41✔
121
        var payments []*pymtpkg.MPPayment
41✔
122

41✔
123
        err := kvdb.View(d, func(tx kvdb.RTx) error {
82✔
124
                paymentsBucket := tx.ReadBucket(paymentsRootBucket)
41✔
125
                if paymentsBucket == nil {
41✔
126
                        return nil
×
127
                }
×
128

129
                return paymentsBucket.ForEach(func(k, v []byte) error {
189✔
130
                        bucket := paymentsBucket.NestedReadBucket(k)
148✔
131
                        if bucket == nil {
148✔
132
                                // We only expect sub-buckets to be found in
×
133
                                // this top-level bucket.
×
134
                                return fmt.Errorf("non bucket element in " +
×
135
                                        "payments bucket")
×
136
                        }
×
137

138
                        p, err := fetchPayment(bucket)
148✔
139
                        if err != nil {
148✔
140
                                return err
×
141
                        }
×
142

143
                        payments = append(payments, p)
148✔
144

148✔
145
                        // For older versions of lnd, duplicate payments to a
148✔
146
                        // payment has was possible. These will be found in a
148✔
147
                        // sub-bucket indexed by their sequence number if
148✔
148
                        // available.
148✔
149
                        duplicatePayments, err := fetchDuplicatePayments(bucket)
148✔
150
                        if err != nil {
148✔
151
                                return err
×
152
                        }
×
153

154
                        payments = append(payments, duplicatePayments...)
148✔
155
                        return nil
148✔
156
                })
157
        }, func() {
41✔
158
                payments = nil
41✔
159
        })
41✔
160
        if err != nil {
41✔
161
                return nil, err
×
162
        }
×
163

164
        // Before returning, sort the payments by their sequence number.
165
        sort.Slice(payments, func(i, j int) bool {
295✔
166
                return payments[i].SequenceNum < payments[j].SequenceNum
254✔
167
        })
254✔
168

169
        return payments, nil
41✔
170
}
171

172
func fetchCreationInfo(bucket kvdb.RBucket) (*pymtpkg.PaymentCreationInfo,
173
        error) {
643✔
174

643✔
175
        b := bucket.Get(paymentCreationInfoKey)
643✔
176
        if b == nil {
643✔
177
                return nil, fmt.Errorf("creation info not found")
×
178
        }
×
179

180
        r := bytes.NewReader(b)
643✔
181
        return deserializePaymentCreationInfo(r)
643✔
182
}
183

184
func fetchPayment(bucket kvdb.RBucket) (*pymtpkg.MPPayment, error) {
643✔
185
        seqBytes := bucket.Get(paymentSequenceKey)
643✔
186
        if seqBytes == nil {
643✔
187
                return nil, fmt.Errorf("sequence number not found")
×
188
        }
×
189

190
        sequenceNum := binary.BigEndian.Uint64(seqBytes)
643✔
191

643✔
192
        // Get the PaymentCreationInfo.
643✔
193
        creationInfo, err := fetchCreationInfo(bucket)
643✔
194
        if err != nil {
643✔
195
                return nil, err
×
196
        }
×
197

198
        var htlcs []pymtpkg.HTLCAttempt
643✔
199
        htlcsBucket := bucket.NestedReadBucket(paymentHtlcsBucket)
643✔
200
        if htlcsBucket != nil {
1,032✔
201
                // Get the payment attempts. This can be empty.
389✔
202
                htlcs, err = fetchHtlcAttempts(htlcsBucket)
389✔
203
                if err != nil {
389✔
204
                        return nil, err
×
205
                }
×
206
        }
207

208
        // Get failure reason if available.
209
        var failureReason *pymtpkg.FailureReason
643✔
210
        b := bucket.Get(paymentFailInfoKey)
643✔
211
        if b != nil {
719✔
212
                reason := pymtpkg.FailureReason(b[0])
76✔
213
                failureReason = &reason
76✔
214
        }
76✔
215

216
        // Create a new payment.
217
        payment := &pymtpkg.MPPayment{
643✔
218
                SequenceNum:   sequenceNum,
643✔
219
                Info:          creationInfo,
643✔
220
                HTLCs:         htlcs,
643✔
221
                FailureReason: failureReason,
643✔
222
        }
643✔
223

643✔
224
        // Set its state and status.
643✔
225
        if err := payment.SetState(); err != nil {
643✔
226
                return nil, err
×
227
        }
×
228

229
        return payment, nil
643✔
230
}
231

232
// fetchHtlcAttempts retrieves all htlc attempts made for the payment found in
233
// the given bucket.
234
func fetchHtlcAttempts(bucket kvdb.RBucket) ([]pymtpkg.HTLCAttempt, error) {
396✔
235
        htlcsMap := make(map[uint64]*pymtpkg.HTLCAttempt)
396✔
236

396✔
237
        attemptInfoCount := 0
396✔
238
        err := bucket.ForEach(func(k, v []byte) error {
1,494✔
239
                aid := byteOrder.Uint64(k[len(k)-8:])
1,098✔
240

1,098✔
241
                if _, ok := htlcsMap[aid]; !ok {
1,802✔
242
                        htlcsMap[aid] = &pymtpkg.HTLCAttempt{}
704✔
243
                }
704✔
244

245
                var err error
1,098✔
246
                switch {
1,098✔
247
                case bytes.HasPrefix(k, htlcAttemptInfoKey):
704✔
248
                        attemptInfo, err := readHtlcAttemptInfo(v)
704✔
249
                        if err != nil {
704✔
250
                                return err
×
251
                        }
×
252

253
                        attemptInfo.AttemptID = aid
704✔
254
                        htlcsMap[aid].HTLCAttemptInfo = *attemptInfo
704✔
255
                        attemptInfoCount++
704✔
256

257
                case bytes.HasPrefix(k, htlcSettleInfoKey):
92✔
258
                        htlcsMap[aid].Settle, err = readHtlcSettleInfo(v)
92✔
259
                        if err != nil {
92✔
260
                                return err
×
261
                        }
×
262

263
                case bytes.HasPrefix(k, htlcFailInfoKey):
308✔
264
                        htlcsMap[aid].Failure, err = readHtlcFailInfo(v)
308✔
265
                        if err != nil {
308✔
266
                                return err
×
267
                        }
×
268

269
                default:
×
270
                        return fmt.Errorf("unknown htlc attempt key")
×
271
                }
272

273
                return nil
1,098✔
274
        })
275
        if err != nil {
396✔
276
                return nil, err
×
277
        }
×
278

279
        // Sanity check that all htlcs have an attempt info.
280
        if attemptInfoCount != len(htlcsMap) {
396✔
NEW
281
                return nil, pymtpkg.ErrNoAttemptInfo
×
282
        }
×
283

284
        keys := make([]uint64, len(htlcsMap))
396✔
285
        i := 0
396✔
286
        for k := range htlcsMap {
1,100✔
287
                keys[i] = k
704✔
288
                i++
704✔
289
        }
704✔
290

291
        // Sort HTLC attempts by their attempt ID. This is needed because in the
292
        // DB we store the attempts with keys prefixed by their status which
293
        // changes order (groups them together by status).
294
        sort.Slice(keys, func(i, j int) bool {
742✔
295
                return keys[i] < keys[j]
346✔
296
        })
346✔
297

298
        htlcs := make([]pymtpkg.HTLCAttempt, len(htlcsMap))
396✔
299
        for i, key := range keys {
1,100✔
300
                htlcs[i] = *htlcsMap[key]
704✔
301
        }
704✔
302

303
        return htlcs, nil
396✔
304
}
305

306
// readHtlcAttemptInfo reads the payment attempt info for this htlc.
307
func readHtlcAttemptInfo(b []byte) (*pymtpkg.HTLCAttemptInfo, error) {
704✔
308
        r := bytes.NewReader(b)
704✔
309
        return deserializeHTLCAttemptInfo(r)
704✔
310
}
704✔
311

312
// readHtlcSettleInfo reads the settle info for the htlc. If the htlc isn't
313
// settled, nil is returned.
314
func readHtlcSettleInfo(b []byte) (*pymtpkg.HTLCSettleInfo, error) {
92✔
315
        r := bytes.NewReader(b)
92✔
316
        return deserializeHTLCSettleInfo(r)
92✔
317
}
92✔
318

319
// readHtlcFailInfo reads the failure info for the htlc. If the htlc hasn't
320
// failed, nil is returned.
321
func readHtlcFailInfo(b []byte) (*pymtpkg.HTLCFailInfo, error) {
308✔
322
        r := bytes.NewReader(b)
308✔
323
        return deserializeHTLCFailInfo(r)
308✔
324
}
308✔
325

326
// fetchFailedHtlcKeys retrieves the bucket keys of all failed HTLCs of a
327
// payment bucket.
328
func fetchFailedHtlcKeys(bucket kvdb.RBucket) ([][]byte, error) {
7✔
329
        htlcsBucket := bucket.NestedReadBucket(paymentHtlcsBucket)
7✔
330

7✔
331
        var htlcs []pymtpkg.HTLCAttempt
7✔
332
        var err error
7✔
333
        if htlcsBucket != nil {
14✔
334
                htlcs, err = fetchHtlcAttempts(htlcsBucket)
7✔
335
                if err != nil {
7✔
336
                        return nil, err
×
337
                }
×
338
        }
339

340
        // Now iterate though them and save the bucket keys for the failed
341
        // HTLCs.
342
        var htlcKeys [][]byte
7✔
343
        for _, h := range htlcs {
19✔
344
                if h.Failure == nil {
15✔
345
                        continue
3✔
346
                }
347

348
                htlcKeyBytes := make([]byte, 8)
9✔
349
                binary.BigEndian.PutUint64(htlcKeyBytes, h.AttemptID)
9✔
350

9✔
351
                htlcKeys = append(htlcKeys, htlcKeyBytes)
9✔
352
        }
353

354
        return htlcKeys, nil
7✔
355
}
356

357
// QueryPayments is a query to the payments database which is restricted
358
// to a subset of payments by the payments query, containing an offset
359
// index and a maximum number of returned payments.
360
func (d *DB) QueryPayments(query pymtpkg.Query) (pymtpkg.Response, error) {
39✔
361
        var resp pymtpkg.Response
39✔
362

39✔
363
        if err := kvdb.View(d, func(tx kvdb.RTx) error {
78✔
364
                // Get the root payments bucket.
39✔
365
                paymentsBucket := tx.ReadBucket(paymentsRootBucket)
39✔
366
                if paymentsBucket == nil {
60✔
367
                        return nil
21✔
368
                }
21✔
369

370
                // Get the index bucket which maps sequence number -> payment
371
                // hash and duplicate bool. If we have a payments bucket, we
372
                // should have an indexes bucket as well.
373
                indexes := tx.ReadBucket(paymentsIndexBucket)
21✔
374
                if indexes == nil {
21✔
375
                        return fmt.Errorf("index bucket does not exist")
×
376
                }
×
377

378
                // accumulatePayments gets payments with the sequence number
379
                // and hash provided and adds them to our list of payments if
380
                // they meet the criteria of our query. It returns the number
381
                // of payments that were added.
382
                accumulatePayments := func(sequenceKey, hash []byte) (bool,
21✔
383
                        error) {
76✔
384

55✔
385
                        r := bytes.NewReader(hash)
55✔
386
                        paymentHash, err := deserializePaymentIndex(r)
55✔
387
                        if err != nil {
55✔
388
                                return false, err
×
389
                        }
×
390

391
                        payment, err := fetchPaymentWithSequenceNumber(
55✔
392
                                tx, paymentHash, sequenceKey,
55✔
393
                        )
55✔
394
                        if err != nil {
55✔
395
                                return false, err
×
396
                        }
×
397

398
                        // To keep compatibility with the old API, we only
399
                        // return non-succeeded payments if requested.
400
                        if payment.Status != pymtpkg.StatusSucceeded &&
55✔
401
                                !query.IncludeIncomplete {
60✔
402

5✔
403
                                return false, err
5✔
404
                        }
5✔
405

406
                        // Get the creation time in Unix seconds, this always
407
                        // rounds down the nanoseconds to full seconds.
408
                        createTime := payment.Info.CreationTime.Unix()
50✔
409

50✔
410
                        // Skip any payments that were created before the
50✔
411
                        // specified time.
50✔
412
                        if createTime < query.CreationDateStart {
62✔
413
                                return false, nil
12✔
414
                        }
12✔
415

416
                        // Skip any payments that were created after the
417
                        // specified time.
418
                        if query.CreationDateEnd != 0 &&
41✔
419
                                createTime > query.CreationDateEnd {
46✔
420

5✔
421
                                return false, nil
5✔
422
                        }
5✔
423

424
                        // At this point, we've exhausted the offset, so we'll
425
                        // begin collecting invoices found within the range.
426
                        resp.Payments = append(resp.Payments, payment)
39✔
427
                        return true, nil
39✔
428
                }
429

430
                // Create a paginator which reads from our sequence index bucket
431
                // with the parameters provided by the payments query.
432
                paginator := newPaginator(
21✔
433
                        indexes.ReadCursor(), query.Reversed, query.IndexOffset,
21✔
434
                        query.MaxPayments,
21✔
435
                )
21✔
436

21✔
437
                // Run a paginated query, adding payments to our response.
21✔
438
                if err := paginator.query(accumulatePayments); err != nil {
21✔
439
                        return err
×
440
                }
×
441

442
                // Counting the total number of payments is expensive, since we
443
                // literally have to traverse the cursor linearly, which can
444
                // take quite a while. So it's an optional query parameter.
445
                if query.CountTotal {
21✔
446
                        var (
×
447
                                totalPayments uint64
×
448
                                err           error
×
449
                        )
×
450
                        countFn := func(_, _ []byte) error {
×
451
                                totalPayments++
×
452

×
453
                                return nil
×
454
                        }
×
455

456
                        // In non-boltdb database backends, there's a faster
457
                        // ForAll query that allows for batch fetching items.
458
                        if fastBucket, ok := indexes.(kvdb.ExtendedRBucket); ok {
×
459
                                err = fastBucket.ForAll(countFn)
×
460
                        } else {
×
461
                                err = indexes.ForEach(countFn)
×
462
                        }
×
463
                        if err != nil {
×
464
                                return fmt.Errorf("error counting payments: %w",
×
465
                                        err)
×
466
                        }
×
467

468
                        resp.TotalCount = totalPayments
×
469
                }
470

471
                return nil
21✔
472
        }, func() {
39✔
473
                resp = pymtpkg.Response{}
39✔
474
        }); err != nil {
39✔
475
                return resp, err
×
476
        }
×
477

478
        // Need to swap the payments slice order if reversed order.
479
        if query.Reversed {
55✔
480
                for l, r := 0, len(resp.Payments)-1; l < r; l, r = l+1, r-1 {
24✔
481
                        resp.Payments[l], resp.Payments[r] =
8✔
482
                                resp.Payments[r], resp.Payments[l]
8✔
483
                }
8✔
484
        }
485

486
        // Set the first and last index of the returned payments so that the
487
        // caller can resume from this point later on.
488
        if len(resp.Payments) > 0 {
58✔
489
                resp.FirstIndexOffset = resp.Payments[0].SequenceNum
19✔
490
                resp.LastIndexOffset =
19✔
491
                        resp.Payments[len(resp.Payments)-1].SequenceNum
19✔
492
        }
19✔
493

494
        return resp, nil
39✔
495
}
496

497
// fetchPaymentWithSequenceNumber get the payment which matches the payment hash
498
// *and* sequence number provided from the database. This is required because
499
// we previously had more than one payment per hash, so we have multiple indexes
500
// pointing to a single payment; we want to retrieve the correct one.
501
func fetchPaymentWithSequenceNumber(tx kvdb.RTx, paymentHash lntypes.Hash,
502
        sequenceNumber []byte) (*pymtpkg.MPPayment, error) {
61✔
503

61✔
504
        // We can now lookup the payment keyed by its hash in
61✔
505
        // the payments root bucket.
61✔
506
        bucket, err := fetchPaymentBucket(tx, paymentHash)
61✔
507
        if err != nil {
61✔
508
                return nil, err
×
509
        }
×
510

511
        // A single payment hash can have multiple payments associated with it.
512
        // We lookup our sequence number first, to determine whether this is
513
        // the payment we are actually looking for.
514
        seqBytes := bucket.Get(paymentSequenceKey)
61✔
515
        if seqBytes == nil {
61✔
NEW
516
                return nil, pymtpkg.ErrNoSequenceNumber
×
517
        }
×
518

519
        // If this top level payment has the sequence number we are looking for,
520
        // return it.
521
        if bytes.Equal(seqBytes, sequenceNumber) {
109✔
522
                return fetchPayment(bucket)
48✔
523
        }
48✔
524

525
        // If we were not looking for the top level payment, we are looking for
526
        // one of our duplicate payments. We need to iterate through the seq
527
        // numbers in this bucket to find the correct payments. If we do not
528
        // find a duplicate payments bucket here, something is wrong.
529
        dup := bucket.NestedReadBucket(duplicatePaymentsBucket)
13✔
530
        if dup == nil {
14✔
531
                return nil, pymtpkg.ErrNoDuplicateBucket
1✔
532
        }
1✔
533

534
        var duplicatePayment *pymtpkg.MPPayment
12✔
535
        err = dup.ForEach(func(k, v []byte) error {
27✔
536
                subBucket := dup.NestedReadBucket(k)
15✔
537
                if subBucket == nil {
15✔
538
                        // We one bucket for each duplicate to be found.
×
NEW
539
                        return pymtpkg.ErrNoDuplicateNestedBucket
×
540
                }
×
541

542
                seqBytes := subBucket.Get(duplicatePaymentSequenceKey)
15✔
543
                if seqBytes == nil {
15✔
544
                        return err
×
545
                }
×
546

547
                // If this duplicate payment is not the sequence number we are
548
                // looking for, we can continue.
549
                if !bytes.Equal(seqBytes, sequenceNumber) {
19✔
550
                        return nil
4✔
551
                }
4✔
552

553
                duplicatePayment, err = fetchDuplicatePayment(subBucket)
11✔
554
                if err != nil {
11✔
555
                        return err
×
556
                }
×
557

558
                return nil
11✔
559
        })
560
        if err != nil {
12✔
561
                return nil, err
×
562
        }
×
563

564
        // If none of the duplicate payments matched our sequence number, we
565
        // failed to find the payment with this sequence number; something is
566
        // wrong.
567
        if duplicatePayment == nil {
13✔
568
                return nil, pymtpkg.ErrDuplicateNotFound
1✔
569
        }
1✔
570

571
        return duplicatePayment, nil
11✔
572
}
573

574
// DeletePayment deletes a payment from the DB given its payment hash. If
575
// failedHtlcsOnly is set, only failed HTLC attempts of the payment will be
576
// deleted.
577
func (d *DB) DeletePayment(paymentHash lntypes.Hash,
578
        failedHtlcsOnly bool) error {
11✔
579

11✔
580
        return kvdb.Update(d, func(tx kvdb.RwTx) error {
22✔
581
                payments := tx.ReadWriteBucket(paymentsRootBucket)
11✔
582
                if payments == nil {
11✔
583
                        return nil
×
584
                }
×
585

586
                bucket := payments.NestedReadWriteBucket(paymentHash[:])
11✔
587
                if bucket == nil {
12✔
588
                        return fmt.Errorf("non bucket element in payments " +
1✔
589
                                "bucket")
1✔
590
                }
1✔
591

592
                // If the status is InFlight, we cannot safely delete
593
                // the payment information, so we return early.
594
                paymentStatus, err := fetchPaymentStatus(bucket)
10✔
595
                if err != nil {
10✔
596
                        return err
×
597
                }
×
598

599
                // If the payment has inflight HTLCs, we cannot safely delete
600
                // the payment information, so we return an error.
601
                if err := paymentStatus.Removable(); err != nil {
13✔
602
                        return fmt.Errorf("payment '%v' has inflight HTLCs"+
3✔
603
                                "and therefore cannot be deleted: %w",
3✔
604
                                paymentHash.String(), err)
3✔
605
                }
3✔
606

607
                // Delete the failed HTLC attempts we found.
608
                if failedHtlcsOnly {
11✔
609
                        toDelete, err := fetchFailedHtlcKeys(bucket)
4✔
610
                        if err != nil {
4✔
611
                                return err
×
612
                        }
×
613

614
                        htlcsBucket := bucket.NestedReadWriteBucket(
4✔
615
                                paymentHtlcsBucket,
4✔
616
                        )
4✔
617

4✔
618
                        for _, htlcID := range toDelete {
10✔
619
                                err = htlcsBucket.Delete(
6✔
620
                                        htlcBucketKey(htlcAttemptInfoKey, htlcID),
6✔
621
                                )
6✔
622
                                if err != nil {
6✔
623
                                        return err
×
624
                                }
×
625

626
                                err = htlcsBucket.Delete(
6✔
627
                                        htlcBucketKey(htlcFailInfoKey, htlcID),
6✔
628
                                )
6✔
629
                                if err != nil {
6✔
630
                                        return err
×
631
                                }
×
632

633
                                err = htlcsBucket.Delete(
6✔
634
                                        htlcBucketKey(htlcSettleInfoKey, htlcID),
6✔
635
                                )
6✔
636
                                if err != nil {
6✔
637
                                        return err
×
638
                                }
×
639
                        }
640

641
                        return nil
4✔
642
                }
643

644
                seqNrs, err := fetchSequenceNumbers(bucket)
3✔
645
                if err != nil {
3✔
646
                        return err
×
647
                }
×
648

649
                if err := payments.DeleteNestedBucket(paymentHash[:]); err != nil {
3✔
650
                        return err
×
651
                }
×
652

653
                indexBucket := tx.ReadWriteBucket(paymentsIndexBucket)
3✔
654
                for _, k := range seqNrs {
6✔
655
                        if err := indexBucket.Delete(k); err != nil {
3✔
656
                                return err
×
657
                        }
×
658
                }
659

660
                return nil
3✔
661
        }, func() {})
11✔
662
}
663

664
// DeletePayments deletes all completed and failed payments from the DB. If
665
// failedOnly is set, only failed payments will be considered for deletion. If
666
// failedHtlcsOnly is set, the payment itself won't be deleted, only failed HTLC
667
// attempts. The method returns the number of deleted payments, which is always
668
// 0 if failedHtlcsOnly is set.
669
func (d *DB) DeletePayments(failedOnly, failedHtlcsOnly bool) (int, error) {
9✔
670
        var numPayments int
9✔
671
        err := kvdb.Update(d, func(tx kvdb.RwTx) error {
18✔
672
                payments := tx.ReadWriteBucket(paymentsRootBucket)
9✔
673
                if payments == nil {
9✔
674
                        return nil
×
675
                }
×
676

677
                var (
9✔
678
                        // deleteBuckets is the set of payment buckets we need
9✔
679
                        // to delete.
9✔
680
                        deleteBuckets [][]byte
9✔
681

9✔
682
                        // deleteIndexes is the set of indexes pointing to these
9✔
683
                        // payments that need to be deleted.
9✔
684
                        deleteIndexes [][]byte
9✔
685

9✔
686
                        // deleteHtlcs maps a payment hash to the HTLC IDs we
9✔
687
                        // want to delete for that payment.
9✔
688
                        deleteHtlcs = make(map[lntypes.Hash][][]byte)
9✔
689
                )
9✔
690
                err := payments.ForEach(func(k, _ []byte) error {
30✔
691
                        bucket := payments.NestedReadBucket(k)
21✔
692
                        if bucket == nil {
21✔
693
                                // We only expect sub-buckets to be found in
×
694
                                // this top-level bucket.
×
695
                                return fmt.Errorf("non bucket element in " +
×
696
                                        "payments bucket")
×
697
                        }
×
698

699
                        // If the status is InFlight, we cannot safely delete
700
                        // the payment information, so we return early.
701
                        paymentStatus, err := fetchPaymentStatus(bucket)
21✔
702
                        if err != nil {
21✔
703
                                return err
×
704
                        }
×
705

706
                        // If the payment has inflight HTLCs, we cannot safely
707
                        // delete the payment information, so we return an nil
708
                        // to skip it.
709
                        if err := paymentStatus.Removable(); err != nil {
27✔
710
                                return nil
6✔
711
                        }
6✔
712

713
                        // If we requested to only delete failed payments, we
714
                        // can return if this one is not.
715
                        if failedOnly && paymentStatus != pymtpkg.StatusFailed {
19✔
716
                                return nil
4✔
717
                        }
4✔
718

719
                        // If we are only deleting failed HTLCs, fetch them.
720
                        if failedHtlcsOnly {
14✔
721
                                toDelete, err := fetchFailedHtlcKeys(bucket)
3✔
722
                                if err != nil {
3✔
723
                                        return err
×
724
                                }
×
725

726
                                hash, err := lntypes.MakeHash(k)
3✔
727
                                if err != nil {
3✔
728
                                        return err
×
729
                                }
×
730

731
                                deleteHtlcs[hash] = toDelete
3✔
732

3✔
733
                                // We return, we are only deleting attempts.
3✔
734
                                return nil
3✔
735
                        }
736

737
                        // Add the bucket to the set of buckets we can delete.
738
                        deleteBuckets = append(deleteBuckets, k)
8✔
739

8✔
740
                        // Get all the sequence number associated with the
8✔
741
                        // payment, including duplicates.
8✔
742
                        seqNrs, err := fetchSequenceNumbers(bucket)
8✔
743
                        if err != nil {
8✔
744
                                return err
×
745
                        }
×
746

747
                        deleteIndexes = append(deleteIndexes, seqNrs...)
8✔
748
                        numPayments++
8✔
749
                        return nil
8✔
750
                })
751
                if err != nil {
9✔
752
                        return err
×
753
                }
×
754

755
                // Delete the failed HTLC attempts we found.
756
                for hash, htlcIDs := range deleteHtlcs {
12✔
757
                        bucket := payments.NestedReadWriteBucket(hash[:])
3✔
758
                        htlcsBucket := bucket.NestedReadWriteBucket(
3✔
759
                                paymentHtlcsBucket,
3✔
760
                        )
3✔
761

3✔
762
                        for _, aid := range htlcIDs {
6✔
763
                                if err := htlcsBucket.Delete(
3✔
764
                                        htlcBucketKey(htlcAttemptInfoKey, aid),
3✔
765
                                ); err != nil {
3✔
766
                                        return err
×
767
                                }
×
768

769
                                if err := htlcsBucket.Delete(
3✔
770
                                        htlcBucketKey(htlcFailInfoKey, aid),
3✔
771
                                ); err != nil {
3✔
772
                                        return err
×
773
                                }
×
774

775
                                if err := htlcsBucket.Delete(
3✔
776
                                        htlcBucketKey(htlcSettleInfoKey, aid),
3✔
777
                                ); err != nil {
3✔
778
                                        return err
×
779
                                }
×
780
                        }
781
                }
782

783
                for _, k := range deleteBuckets {
17✔
784
                        if err := payments.DeleteNestedBucket(k); err != nil {
8✔
785
                                return err
×
786
                        }
×
787
                }
788

789
                // Get our index bucket and delete all indexes pointing to the
790
                // payments we are deleting.
791
                indexBucket := tx.ReadWriteBucket(paymentsIndexBucket)
9✔
792
                for _, k := range deleteIndexes {
18✔
793
                        if err := indexBucket.Delete(k); err != nil {
9✔
794
                                return err
×
795
                        }
×
796
                }
797

798
                return nil
9✔
799
        }, func() {
9✔
800
                numPayments = 0
9✔
801
        })
9✔
802
        if err != nil {
9✔
803
                return 0, err
×
804
        }
×
805

806
        return numPayments, nil
9✔
807
}
808

809
// fetchSequenceNumbers fetches all the sequence numbers associated with a
810
// payment, including those belonging to any duplicate payments.
811
func fetchSequenceNumbers(paymentBucket kvdb.RBucket) ([][]byte, error) {
11✔
812
        seqNum := paymentBucket.Get(paymentSequenceKey)
11✔
813
        if seqNum == nil {
11✔
814
                return nil, errors.New("expected sequence number")
×
815
        }
×
816

817
        sequenceNumbers := [][]byte{seqNum}
11✔
818

11✔
819
        // Get the duplicate payments bucket, if it has no duplicates, just
11✔
820
        // return early with the payment sequence number.
11✔
821
        duplicates := paymentBucket.NestedReadBucket(duplicatePaymentsBucket)
11✔
822
        if duplicates == nil {
21✔
823
                return sequenceNumbers, nil
10✔
824
        }
10✔
825

826
        // If we do have duplicated, they are keyed by sequence number, so we
827
        // iterate through the duplicates bucket and add them to our set of
828
        // sequence numbers.
829
        if err := duplicates.ForEach(func(k, v []byte) error {
2✔
830
                sequenceNumbers = append(sequenceNumbers, k)
1✔
831
                return nil
1✔
832
        }); err != nil {
1✔
833
                return nil, err
×
834
        }
×
835

836
        return sequenceNumbers, nil
1✔
837
}
838

839
// nolint: dupl
840
func serializePaymentCreationInfo(w io.Writer,
841
        c *pymtpkg.PaymentCreationInfo) error {
154✔
842

154✔
843
        var scratch [8]byte
154✔
844

154✔
845
        if _, err := w.Write(c.PaymentIdentifier[:]); err != nil {
154✔
846
                return err
×
847
        }
×
848

849
        byteOrder.PutUint64(scratch[:], uint64(c.Value))
154✔
850
        if _, err := w.Write(scratch[:]); err != nil {
154✔
851
                return err
×
852
        }
×
853

854
        if err := serializeTime(w, c.CreationTime); err != nil {
154✔
855
                return err
×
856
        }
×
857

858
        byteOrder.PutUint32(scratch[:4], uint32(len(c.PaymentRequest)))
154✔
859
        if _, err := w.Write(scratch[:4]); err != nil {
154✔
860
                return err
×
861
        }
×
862

863
        if _, err := w.Write(c.PaymentRequest[:]); err != nil {
154✔
864
                return err
×
865
        }
×
866

867
        // Any remaining bytes are TLV encoded records. Currently, these are
868
        // only the custom records provided by the user to be sent to the first
869
        // hop. But this can easily be extended with further records by merging
870
        // the records into a single TLV stream.
871
        err := c.FirstHopCustomRecords.SerializeTo(w)
154✔
872
        if err != nil {
154✔
873
                return err
×
874
        }
×
875

876
        return nil
154✔
877
}
878

879
func deserializePaymentCreationInfo(r io.Reader) (*pymtpkg.PaymentCreationInfo,
880
        error) {
645✔
881

645✔
882
        var scratch [8]byte
645✔
883

645✔
884
        c := &pymtpkg.PaymentCreationInfo{}
645✔
885

645✔
886
        if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil {
645✔
887
                return nil, err
×
888
        }
×
889

890
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
645✔
891
                return nil, err
×
892
        }
×
893
        c.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
645✔
894

645✔
895
        creationTime, err := deserializeTime(r)
645✔
896
        if err != nil {
645✔
897
                return nil, err
×
898
        }
×
899
        c.CreationTime = creationTime
645✔
900

645✔
901
        if _, err := io.ReadFull(r, scratch[:4]); err != nil {
645✔
902
                return nil, err
×
903
        }
×
904

905
        reqLen := uint32(byteOrder.Uint32(scratch[:4]))
645✔
906
        payReq := make([]byte, reqLen)
645✔
907
        if reqLen > 0 {
1,290✔
908
                if _, err := io.ReadFull(r, payReq); err != nil {
645✔
909
                        return nil, err
×
910
                }
×
911
        }
912
        c.PaymentRequest = payReq
645✔
913

645✔
914
        // Any remaining bytes are TLV encoded records. Currently, these are
645✔
915
        // only the custom records provided by the user to be sent to the first
645✔
916
        // hop. But this can easily be extended with further records by merging
645✔
917
        // the records into a single TLV stream.
645✔
918
        c.FirstHopCustomRecords, err = lnwire.ParseCustomRecordsFrom(r)
645✔
919
        if err != nil {
645✔
920
                return nil, err
×
921
        }
×
922

923
        return c, nil
645✔
924
}
925

926
func serializeHTLCAttemptInfo(w io.Writer, a *pymtpkg.HTLCAttemptInfo) error {
75✔
927
        sessionKey := a.SessionKey().Serialize()
75✔
928

75✔
929
        // We nned to make sure the session key is 32 bytes, so we copy the
75✔
930
        // session key into a 32 byte array.
75✔
931
        var sessionKeyArray [32]byte
75✔
932
        copy(sessionKeyArray[:], sessionKey)
75✔
933
        if err := WriteElements(w, sessionKeyArray); err != nil {
75✔
934
                return err
×
935
        }
×
936

937
        if err := SerializeRoute(w, a.Route); err != nil {
75✔
938
                return err
×
939
        }
×
940

941
        if err := serializeTime(w, a.AttemptTime); err != nil {
75✔
942
                return err
×
943
        }
×
944

945
        // If the hash is nil we can just return.
946
        if a.Hash == nil {
75✔
947
                return nil
×
948
        }
×
949

950
        if _, err := w.Write(a.Hash[:]); err != nil {
75✔
951
                return err
×
952
        }
×
953

954
        // Merge the fixed/known records together with the custom records to
955
        // serialize them as a single blob. We can't do this in SerializeRoute
956
        // because we're in the middle of the byte stream there. We can only do
957
        // TLV serialization at the end of the stream, since EOF is allowed for
958
        // a stream if no more data is expected.
959
        producers := []tlv.RecordProducer{
75✔
960
                &a.Route.FirstHopAmount,
75✔
961
        }
75✔
962
        tlvData, err := lnwire.MergeAndEncode(
75✔
963
                producers, nil, a.Route.FirstHopWireCustomRecords,
75✔
964
        )
75✔
965
        if err != nil {
75✔
966
                return err
×
967
        }
×
968

969
        if _, err := w.Write(tlvData); err != nil {
75✔
970
                return err
×
971
        }
×
972

973
        return nil
75✔
974
}
975

976
func deserializeHTLCAttemptInfo(r io.Reader) (*pymtpkg.HTLCAttemptInfo, error) {
706✔
977
        var sessionKey [32]byte
706✔
978
        err := ReadElements(r, &sessionKey)
706✔
979
        if err != nil {
706✔
980
                return nil, err
×
981
        }
×
982

983
        a := &pymtpkg.HTLCAttemptInfo{}
706✔
984
        a.SetSessionKey(sessionKey)
706✔
985

706✔
986
        a.Route, err = DeserializeRoute(r)
706✔
987
        if err != nil {
706✔
NEW
988

×
989
                return nil, err
×
990
        }
×
991

992
        a.AttemptTime, err = deserializeTime(r)
706✔
993
        if err != nil {
706✔
994
                return nil, err
×
995
        }
×
996

997
        hash := lntypes.Hash{}
706✔
998
        _, err = io.ReadFull(r, hash[:])
706✔
999

706✔
1000
        switch {
706✔
1001
        // Older payment attempts wouldn't have the hash set, in which case we
1002
        // can just return.
1003
        case err == io.EOF, err == io.ErrUnexpectedEOF:
×
1004
                return a, nil
×
1005

1006
        case err != nil:
×
1007
                return nil, err
×
1008

1009
        default:
706✔
1010
        }
1011

1012
        a.Hash = &hash
706✔
1013

706✔
1014
        // Read any remaining data (if any) and parse it into the known records
706✔
1015
        // and custom records.
706✔
1016
        extraData, err := io.ReadAll(r)
706✔
1017
        if err != nil {
706✔
1018
                return nil, err
×
1019
        }
×
1020

1021
        customRecords, _, _, err := lnwire.ParseAndExtractCustomRecords(
706✔
1022
                extraData, &a.Route.FirstHopAmount,
706✔
1023
        )
706✔
1024
        if err != nil {
706✔
1025
                return nil, err
×
1026
        }
×
1027

1028
        a.Route.FirstHopWireCustomRecords = customRecords
706✔
1029

706✔
1030
        return a, nil
706✔
1031
}
1032

1033
func serializeHop(w io.Writer, h *route.Hop) error {
152✔
1034
        if err := WriteElements(w,
152✔
1035
                h.PubKeyBytes[:],
152✔
1036
                h.ChannelID,
152✔
1037
                h.OutgoingTimeLock,
152✔
1038
                h.AmtToForward,
152✔
1039
        ); err != nil {
152✔
1040
                return err
×
1041
        }
×
1042

1043
        if err := binary.Write(w, byteOrder, h.LegacyPayload); err != nil {
152✔
1044
                return err
×
1045
        }
×
1046

1047
        // For legacy payloads, we don't need to write any TLV records, so
1048
        // we'll write a zero indicating the our serialized TLV map has no
1049
        // records.
1050
        if h.LegacyPayload {
232✔
1051
                return WriteElements(w, uint32(0))
80✔
1052
        }
80✔
1053

1054
        // Gather all non-primitive TLV records so that they can be serialized
1055
        // as a single blob.
1056
        //
1057
        // TODO(conner): add migration to unify all fields in a single TLV
1058
        // blobs. The split approach will cause headaches down the road as more
1059
        // fields are added, which we can avoid by having a single TLV stream
1060
        // for all payload fields.
1061
        var records []tlv.Record
72✔
1062
        if h.MPP != nil {
139✔
1063
                records = append(records, h.MPP.Record())
67✔
1064
        }
67✔
1065

1066
        // Add blinding point and encrypted data if present.
1067
        if h.EncryptedData != nil {
77✔
1068
                records = append(records, record.NewEncryptedDataRecord(
5✔
1069
                        &h.EncryptedData,
5✔
1070
                ))
5✔
1071
        }
5✔
1072

1073
        if h.BlindingPoint != nil {
76✔
1074
                records = append(records, record.NewBlindingPointRecord(
4✔
1075
                        &h.BlindingPoint,
4✔
1076
                ))
4✔
1077
        }
4✔
1078

1079
        if h.AMP != nil {
75✔
1080
                records = append(records, h.AMP.Record())
3✔
1081
        }
3✔
1082

1083
        if h.Metadata != nil {
139✔
1084
                records = append(records, record.NewMetadataRecord(&h.Metadata))
67✔
1085
        }
67✔
1086

1087
        if h.TotalAmtMsat != 0 {
76✔
1088
                totalMsatInt := uint64(h.TotalAmtMsat)
4✔
1089
                records = append(
4✔
1090
                        records, record.NewTotalAmtMsatBlinded(&totalMsatInt),
4✔
1091
                )
4✔
1092
        }
4✔
1093

1094
        // Final sanity check to absolutely rule out custom records that are not
1095
        // custom and write into the standard range.
1096
        if err := h.CustomRecords.Validate(); err != nil {
72✔
1097
                return err
×
1098
        }
×
1099

1100
        // Convert custom records to tlv and add to the record list.
1101
        // MapToRecords sorts the list, so adding it here will keep the list
1102
        // canonical.
1103
        tlvRecords := tlv.MapToRecords(h.CustomRecords)
72✔
1104
        records = append(records, tlvRecords...)
72✔
1105

72✔
1106
        // Otherwise, we'll transform our slice of records into a map of the
72✔
1107
        // raw bytes, then serialize them in-line with a length (number of
72✔
1108
        // elements) prefix.
72✔
1109
        mapRecords, err := tlv.RecordsToMap(records)
72✔
1110
        if err != nil {
72✔
1111
                return err
×
1112
        }
×
1113

1114
        numRecords := uint32(len(mapRecords))
72✔
1115
        if err := WriteElements(w, numRecords); err != nil {
72✔
1116
                return err
×
1117
        }
×
1118

1119
        for recordType, rawBytes := range mapRecords {
342✔
1120
                if err := WriteElements(w, recordType); err != nil {
270✔
1121
                        return err
×
1122
                }
×
1123

1124
                if err := wire.WriteVarBytes(w, 0, rawBytes); err != nil {
270✔
1125
                        return err
×
1126
                }
×
1127
        }
1128

1129
        return nil
72✔
1130
}
1131

1132
// maxOnionPayloadSize is the largest Sphinx payload possible, so we don't need
1133
// to read/write a TLV stream larger than this.
1134
const maxOnionPayloadSize = 1300
1135

1136
func deserializeHop(r io.Reader) (*route.Hop, error) {
1,414✔
1137
        h := &route.Hop{}
1,414✔
1138

1,414✔
1139
        var pub []byte
1,414✔
1140
        if err := ReadElements(r, &pub); err != nil {
1,414✔
1141
                return nil, err
×
1142
        }
×
1143
        copy(h.PubKeyBytes[:], pub)
1,414✔
1144

1,414✔
1145
        if err := ReadElements(r,
1,414✔
1146
                &h.ChannelID, &h.OutgoingTimeLock, &h.AmtToForward,
1,414✔
1147
        ); err != nil {
1,414✔
1148
                return nil, err
×
1149
        }
×
1150

1151
        // TODO(roasbeef): change field to allow LegacyPayload false to be the
1152
        // legacy default?
1153
        err := binary.Read(r, byteOrder, &h.LegacyPayload)
1,414✔
1154
        if err != nil {
1,414✔
1155
                return nil, err
×
1156
        }
×
1157

1158
        var numElements uint32
1,414✔
1159
        if err := ReadElements(r, &numElements); err != nil {
1,414✔
1160
                return nil, err
×
1161
        }
×
1162

1163
        // If there're no elements, then we can return early.
1164
        if numElements == 0 {
2,149✔
1165
                return h, nil
735✔
1166
        }
735✔
1167

1168
        tlvMap := make(map[uint64][]byte)
682✔
1169
        for i := uint32(0); i < numElements; i++ {
3,391✔
1170
                var tlvType uint64
2,709✔
1171
                if err := ReadElements(r, &tlvType); err != nil {
2,709✔
1172
                        return nil, err
×
1173
                }
×
1174

1175
                rawRecordBytes, err := wire.ReadVarBytes(
2,709✔
1176
                        r, 0, maxOnionPayloadSize, "tlv",
2,709✔
1177
                )
2,709✔
1178
                if err != nil {
2,709✔
1179
                        return nil, err
×
1180
                }
×
1181

1182
                tlvMap[tlvType] = rawRecordBytes
2,709✔
1183
        }
1184

1185
        // If the MPP type is present, remove it from the generic TLV map and
1186
        // parse it back into a proper MPP struct.
1187
        //
1188
        // TODO(conner): add migration to unify all fields in a single TLV
1189
        // blobs. The split approach will cause headaches down the road as more
1190
        // fields are added, which we can avoid by having a single TLV stream
1191
        // for all payload fields.
1192
        mppType := uint64(record.MPPOnionType)
682✔
1193
        if mppBytes, ok := tlvMap[mppType]; ok {
1,358✔
1194
                delete(tlvMap, mppType)
676✔
1195

676✔
1196
                var (
676✔
1197
                        mpp    = &record.MPP{}
676✔
1198
                        mppRec = mpp.Record()
676✔
1199
                        r      = bytes.NewReader(mppBytes)
676✔
1200
                )
676✔
1201
                err := mppRec.Decode(r, uint64(len(mppBytes)))
676✔
1202
                if err != nil {
676✔
1203
                        return nil, err
×
1204
                }
×
1205
                h.MPP = mpp
676✔
1206
        }
1207

1208
        // If encrypted data or blinding key are present, remove them from
1209
        // the TLV map and parse into proper types.
1210
        encryptedDataType := uint64(record.EncryptedDataOnionType)
682✔
1211
        if data, ok := tlvMap[encryptedDataType]; ok {
687✔
1212
                delete(tlvMap, encryptedDataType)
5✔
1213
                h.EncryptedData = data
5✔
1214
        }
5✔
1215

1216
        blindingType := uint64(record.BlindingPointOnionType)
682✔
1217
        if blindingPoint, ok := tlvMap[blindingType]; ok {
686✔
1218
                delete(tlvMap, blindingType)
4✔
1219

4✔
1220
                h.BlindingPoint, err = btcec.ParsePubKey(blindingPoint)
4✔
1221
                if err != nil {
4✔
1222
                        return nil, fmt.Errorf("invalid blinding point: %w",
×
1223
                                err)
×
1224
                }
×
1225
        }
1226

1227
        ampType := uint64(record.AMPOnionType)
682✔
1228
        if ampBytes, ok := tlvMap[ampType]; ok {
685✔
1229
                delete(tlvMap, ampType)
3✔
1230

3✔
1231
                var (
3✔
1232
                        amp    = &record.AMP{}
3✔
1233
                        ampRec = amp.Record()
3✔
1234
                        r      = bytes.NewReader(ampBytes)
3✔
1235
                )
3✔
1236
                err := ampRec.Decode(r, uint64(len(ampBytes)))
3✔
1237
                if err != nil {
3✔
1238
                        return nil, err
×
1239
                }
×
1240
                h.AMP = amp
3✔
1241
        }
1242

1243
        // If the metadata type is present, remove it from the tlv map and
1244
        // populate directly on the hop.
1245
        metadataType := uint64(record.MetadataOnionType)
682✔
1246
        if metadata, ok := tlvMap[metadataType]; ok {
1,359✔
1247
                delete(tlvMap, metadataType)
677✔
1248

677✔
1249
                h.Metadata = metadata
677✔
1250
        }
677✔
1251

1252
        totalAmtMsatType := uint64(record.TotalAmtMsatBlindedType)
682✔
1253
        if totalAmtMsat, ok := tlvMap[totalAmtMsatType]; ok {
686✔
1254
                delete(tlvMap, totalAmtMsatType)
4✔
1255

4✔
1256
                var (
4✔
1257
                        totalAmtMsatInt uint64
4✔
1258
                        buf             [8]byte
4✔
1259
                )
4✔
1260
                if err := tlv.DTUint64(
4✔
1261
                        bytes.NewReader(totalAmtMsat),
4✔
1262
                        &totalAmtMsatInt,
4✔
1263
                        &buf,
4✔
1264
                        uint64(len(totalAmtMsat)),
4✔
1265
                ); err != nil {
4✔
1266
                        return nil, err
×
1267
                }
×
1268

1269
                h.TotalAmtMsat = lnwire.MilliSatoshi(totalAmtMsatInt)
4✔
1270
        }
1271

1272
        h.CustomRecords = tlvMap
682✔
1273

682✔
1274
        return h, nil
682✔
1275
}
1276

1277
// SerializeRoute serializes a route.
1278
func SerializeRoute(w io.Writer, r route.Route) error {
77✔
1279
        if err := WriteElements(w,
77✔
1280
                r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
77✔
1281
        ); err != nil {
77✔
1282
                return err
×
1283
        }
×
1284

1285
        if err := WriteElements(w, uint32(len(r.Hops))); err != nil {
77✔
1286
                return err
×
1287
        }
×
1288

1289
        for _, h := range r.Hops {
229✔
1290
                if err := serializeHop(w, h); err != nil {
152✔
1291
                        return err
×
1292
                }
×
1293
        }
1294

1295
        // Any new/extra TLV data is encoded in serializeHTLCAttemptInfo!
1296

1297
        return nil
77✔
1298
}
1299

1300
// DeserializeRoute deserializes a route.
1301
func DeserializeRoute(r io.Reader) (route.Route, error) {
708✔
1302
        rt := route.Route{}
708✔
1303
        if err := ReadElements(r,
708✔
1304
                &rt.TotalTimeLock, &rt.TotalAmount,
708✔
1305
        ); err != nil {
708✔
1306
                return rt, err
×
1307
        }
×
1308

1309
        var pub []byte
708✔
1310
        if err := ReadElements(r, &pub); err != nil {
708✔
1311
                return rt, err
×
1312
        }
×
1313
        copy(rt.SourcePubKey[:], pub)
708✔
1314

708✔
1315
        var numHops uint32
708✔
1316
        if err := ReadElements(r, &numHops); err != nil {
708✔
1317
                return rt, err
×
1318
        }
×
1319

1320
        var hops []*route.Hop
708✔
1321
        for i := uint32(0); i < numHops; i++ {
2,122✔
1322
                hop, err := deserializeHop(r)
1,414✔
1323
                if err != nil {
1,414✔
1324
                        return rt, err
×
1325
                }
×
1326
                hops = append(hops, hop)
1,414✔
1327
        }
1328
        rt.Hops = hops
708✔
1329

708✔
1330
        // Any new/extra TLV data is decoded in deserializeHTLCAttemptInfo!
708✔
1331

708✔
1332
        return rt, nil
708✔
1333
}
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