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

lightningnetwork / lnd / 15574102646

11 Jun 2025 01:44AM UTC coverage: 68.554% (+9.9%) from 58.637%
15574102646

Pull #9652

github

web-flow
Merge eb863e46a into 92a5d35cf
Pull Request #9652: lnwallet/chancloser: fix flake in TestRbfCloseClosingNegotiationLocal

11 of 12 new or added lines in 1 file covered. (91.67%)

7276 existing lines in 84 files now uncovered.

134508 of 196208 relevant lines covered (68.55%)

44569.29 hits per line

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

73.42
/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 {
86✔
160
        return r.String()
86✔
161
}
86✔
162

163
// String returns a human-readable FailureReason.
164
func (r FailureReason) String() string {
86✔
165
        switch r {
86✔
166
        case FailureReasonTimeout:
24✔
167
                return "timeout"
24✔
168
        case FailureReasonNoRoute:
22✔
169
                return "no_route"
22✔
170
        case FailureReasonError:
28✔
171
                return "error"
28✔
172
        case FailureReasonPaymentDetails:
10✔
173
                return "incorrect_payment_details"
10✔
174
        case FailureReasonInsufficientBalance:
6✔
175
                return "insufficient_balance"
6✔
176
        case FailureReasonCanceled:
8✔
177
                return "canceled"
8✔
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
// String returns a human-readable description of the payment creation info.
206
func (p *PaymentCreationInfo) String() string {
16✔
207
        return fmt.Sprintf("payment_id=%v, amount=%v, created_at=%v",
16✔
208
                p.PaymentIdentifier, p.Value, p.CreationTime)
16✔
209
}
16✔
210

211
// htlcBucketKey creates a composite key from prefix and id where the result is
212
// simply the two concatenated.
213
func htlcBucketKey(prefix, id []byte) []byte {
536✔
214
        key := make([]byte, len(prefix)+len(id))
536✔
215
        copy(key, prefix)
536✔
216
        copy(key[len(prefix):], id)
536✔
217
        return key
536✔
218
}
536✔
219

220
// FetchPayments returns all sent payments found in the DB.
221
//
222
// nolint: dupl
223
func (d *DB) FetchPayments() ([]*MPPayment, error) {
82✔
224
        var payments []*MPPayment
82✔
225

82✔
226
        err := kvdb.View(d, func(tx kvdb.RTx) error {
164✔
227
                paymentsBucket := tx.ReadBucket(paymentsRootBucket)
82✔
228
                if paymentsBucket == nil {
82✔
229
                        return nil
×
230
                }
×
231

232
                return paymentsBucket.ForEach(func(k, v []byte) error {
378✔
233
                        bucket := paymentsBucket.NestedReadBucket(k)
296✔
234
                        if bucket == nil {
296✔
235
                                // We only expect sub-buckets to be found in
×
236
                                // this top-level bucket.
×
237
                                return fmt.Errorf("non bucket element in " +
×
238
                                        "payments bucket")
×
UNCOV
239
                        }
×
240

241
                        p, err := fetchPayment(bucket)
296✔
242
                        if err != nil {
296✔
243
                                return err
×
244
                        }
×
245

246
                        payments = append(payments, p)
296✔
247

296✔
248
                        // For older versions of lnd, duplicate payments to a
296✔
249
                        // payment has was possible. These will be found in a
296✔
250
                        // sub-bucket indexed by their sequence number if
296✔
251
                        // available.
296✔
252
                        duplicatePayments, err := fetchDuplicatePayments(bucket)
296✔
253
                        if err != nil {
296✔
254
                                return err
×
255
                        }
×
256

257
                        payments = append(payments, duplicatePayments...)
296✔
258
                        return nil
296✔
259
                })
260
        }, func() {
82✔
261
                payments = nil
82✔
262
        })
82✔
263
        if err != nil {
82✔
264
                return nil, err
×
UNCOV
265
        }
×
266

267
        // Before returning, sort the payments by their sequence number.
268
        sort.Slice(payments, func(i, j int) bool {
561✔
269
                return payments[i].SequenceNum < payments[j].SequenceNum
479✔
270
        })
479✔
271

272
        return payments, nil
82✔
273
}
274

275
func fetchCreationInfo(bucket kvdb.RBucket) (*PaymentCreationInfo, error) {
1,286✔
276
        b := bucket.Get(paymentCreationInfoKey)
1,286✔
277
        if b == nil {
1,286✔
UNCOV
278
                return nil, fmt.Errorf("creation info not found")
×
UNCOV
279
        }
×
280

281
        r := bytes.NewReader(b)
1,286✔
282
        return deserializePaymentCreationInfo(r)
1,286✔
283
}
284

285
func fetchPayment(bucket kvdb.RBucket) (*MPPayment, error) {
1,286✔
286
        seqBytes := bucket.Get(paymentSequenceKey)
1,286✔
287
        if seqBytes == nil {
1,286✔
UNCOV
288
                return nil, fmt.Errorf("sequence number not found")
×
UNCOV
289
        }
×
290

291
        sequenceNum := binary.BigEndian.Uint64(seqBytes)
1,286✔
292

1,286✔
293
        // Get the PaymentCreationInfo.
1,286✔
294
        creationInfo, err := fetchCreationInfo(bucket)
1,286✔
295
        if err != nil {
1,286✔
UNCOV
296
                return nil, err
×
UNCOV
297
        }
×
298

299
        var htlcs []HTLCAttempt
1,286✔
300
        htlcsBucket := bucket.NestedReadBucket(paymentHtlcsBucket)
1,286✔
301
        if htlcsBucket != nil {
2,064✔
302
                // Get the payment attempts. This can be empty.
778✔
303
                htlcs, err = fetchHtlcAttempts(htlcsBucket)
778✔
304
                if err != nil {
778✔
UNCOV
305
                        return nil, err
×
UNCOV
306
                }
×
307
        }
308

309
        // Get failure reason if available.
310
        var failureReason *FailureReason
1,286✔
311
        b := bucket.Get(paymentFailInfoKey)
1,286✔
312
        if b != nil {
1,438✔
313
                reason := FailureReason(b[0])
152✔
314
                failureReason = &reason
152✔
315
        }
152✔
316

317
        // Create a new payment.
318
        payment := &MPPayment{
1,286✔
319
                SequenceNum:   sequenceNum,
1,286✔
320
                Info:          creationInfo,
1,286✔
321
                HTLCs:         htlcs,
1,286✔
322
                FailureReason: failureReason,
1,286✔
323
        }
1,286✔
324

1,286✔
325
        // Set its state and status.
1,286✔
326
        if err := payment.setState(); err != nil {
1,286✔
UNCOV
327
                return nil, err
×
UNCOV
328
        }
×
329

330
        return payment, nil
1,286✔
331
}
332

333
// fetchHtlcAttempts retrieves all htlc attempts made for the payment found in
334
// the given bucket.
335
func fetchHtlcAttempts(bucket kvdb.RBucket) ([]HTLCAttempt, error) {
792✔
336
        htlcsMap := make(map[uint64]*HTLCAttempt)
792✔
337

792✔
338
        attemptInfoCount := 0
792✔
339
        err := bucket.ForEach(func(k, v []byte) error {
2,988✔
340
                aid := byteOrder.Uint64(k[len(k)-8:])
2,196✔
341

2,196✔
342
                if _, ok := htlcsMap[aid]; !ok {
3,604✔
343
                        htlcsMap[aid] = &HTLCAttempt{}
1,408✔
344
                }
1,408✔
345

346
                var err error
2,196✔
347
                switch {
2,196✔
348
                case bytes.HasPrefix(k, htlcAttemptInfoKey):
1,408✔
349
                        attemptInfo, err := readHtlcAttemptInfo(v)
1,408✔
350
                        if err != nil {
1,408✔
UNCOV
351
                                return err
×
UNCOV
352
                        }
×
353

354
                        attemptInfo.AttemptID = aid
1,408✔
355
                        htlcsMap[aid].HTLCAttemptInfo = *attemptInfo
1,408✔
356
                        attemptInfoCount++
1,408✔
357

358
                case bytes.HasPrefix(k, htlcSettleInfoKey):
184✔
359
                        htlcsMap[aid].Settle, err = readHtlcSettleInfo(v)
184✔
360
                        if err != nil {
184✔
361
                                return err
×
362
                        }
×
363

364
                case bytes.HasPrefix(k, htlcFailInfoKey):
616✔
365
                        htlcsMap[aid].Failure, err = readHtlcFailInfo(v)
616✔
366
                        if err != nil {
616✔
UNCOV
367
                                return err
×
UNCOV
368
                        }
×
369

UNCOV
370
                default:
×
371
                        return fmt.Errorf("unknown htlc attempt key")
×
372
                }
373

374
                return nil
2,196✔
375
        })
376
        if err != nil {
792✔
377
                return nil, err
×
UNCOV
378
        }
×
379

380
        // Sanity check that all htlcs have an attempt info.
381
        if attemptInfoCount != len(htlcsMap) {
792✔
UNCOV
382
                return nil, errNoAttemptInfo
×
UNCOV
383
        }
×
384

385
        keys := make([]uint64, len(htlcsMap))
792✔
386
        i := 0
792✔
387
        for k := range htlcsMap {
2,200✔
388
                keys[i] = k
1,408✔
389
                i++
1,408✔
390
        }
1,408✔
391

392
        // Sort HTLC attempts by their attempt ID. This is needed because in the
393
        // DB we store the attempts with keys prefixed by their status which
394
        // changes order (groups them together by status).
395
        sort.Slice(keys, func(i, j int) bool {
1,472✔
396
                return keys[i] < keys[j]
680✔
397
        })
680✔
398

399
        htlcs := make([]HTLCAttempt, len(htlcsMap))
792✔
400
        for i, key := range keys {
2,200✔
401
                htlcs[i] = *htlcsMap[key]
1,408✔
402
        }
1,408✔
403

404
        return htlcs, nil
792✔
405
}
406

407
// readHtlcAttemptInfo reads the payment attempt info for this htlc.
408
func readHtlcAttemptInfo(b []byte) (*HTLCAttemptInfo, error) {
1,408✔
409
        r := bytes.NewReader(b)
1,408✔
410
        return deserializeHTLCAttemptInfo(r)
1,408✔
411
}
1,408✔
412

413
// readHtlcSettleInfo reads the settle info for the htlc. If the htlc isn't
414
// settled, nil is returned.
415
func readHtlcSettleInfo(b []byte) (*HTLCSettleInfo, error) {
184✔
416
        r := bytes.NewReader(b)
184✔
417
        return deserializeHTLCSettleInfo(r)
184✔
418
}
184✔
419

420
// readHtlcFailInfo reads the failure info for the htlc. If the htlc hasn't
421
// failed, nil is returned.
422
func readHtlcFailInfo(b []byte) (*HTLCFailInfo, error) {
616✔
423
        r := bytes.NewReader(b)
616✔
424
        return deserializeHTLCFailInfo(r)
616✔
425
}
616✔
426

427
// fetchFailedHtlcKeys retrieves the bucket keys of all failed HTLCs of a
428
// payment bucket.
429
func fetchFailedHtlcKeys(bucket kvdb.RBucket) ([][]byte, error) {
14✔
430
        htlcsBucket := bucket.NestedReadBucket(paymentHtlcsBucket)
14✔
431

14✔
432
        var htlcs []HTLCAttempt
14✔
433
        var err error
14✔
434
        if htlcsBucket != nil {
28✔
435
                htlcs, err = fetchHtlcAttempts(htlcsBucket)
14✔
436
                if err != nil {
14✔
437
                        return nil, err
×
438
                }
×
439
        }
440

441
        // Now iterate though them and save the bucket keys for the failed
442
        // HTLCs.
443
        var htlcKeys [][]byte
14✔
444
        for _, h := range htlcs {
38✔
445
                if h.Failure == nil {
30✔
446
                        continue
6✔
447
                }
448

449
                htlcKeyBytes := make([]byte, 8)
18✔
450
                binary.BigEndian.PutUint64(htlcKeyBytes, h.AttemptID)
18✔
451

18✔
452
                htlcKeys = append(htlcKeys, htlcKeyBytes)
18✔
453
        }
454

455
        return htlcKeys, nil
14✔
456
}
457

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

470
        // MaxPayments is the maximal number of payments returned in the
471
        // payments query.
472
        MaxPayments uint64
473

474
        // Reversed gives a meaning to the IndexOffset. If reversed is set to
475
        // true, the query will fetch payments with indices lower than the
476
        // IndexOffset, otherwise, it will return payments with indices greater
477
        // than the IndexOffset.
478
        Reversed bool
479

480
        // If IncludeIncomplete is true, then return payments that have not yet
481
        // fully completed. This means that pending payments, as well as failed
482
        // payments will show up if this field is set to true.
483
        IncludeIncomplete bool
484

485
        // CountTotal indicates that all payments currently present in the
486
        // payment index (complete and incomplete) should be counted.
487
        CountTotal bool
488

489
        // CreationDateStart, expressed in Unix seconds, if set, filters out
490
        // all payments with a creation date greater than or equal to it.
491
        CreationDateStart int64
492

493
        // CreationDateEnd, expressed in Unix seconds, if set, filters out all
494
        // payments with a creation date less than or equal to it.
495
        CreationDateEnd int64
496
}
497

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

508
        // FirstIndexOffset is the index of the first 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 reverse pagination.
512
        FirstIndexOffset uint64
513

514
        // LastIndexOffset is the index of the last element in the set of
515
        // returned MPPayments. Callers can use this to resume their query
516
        // in the event that the slice has too many events to fit into a single
517
        // response. The offset can be used to continue forward pagination.
518
        LastIndexOffset uint64
519

520
        // TotalCount represents the total number of payments that are currently
521
        // stored in the payment database. This will only be set if the
522
        // CountTotal field in the query was set to true.
523
        TotalCount uint64
524
}
525

526
// QueryPayments is a query to the payments database which is restricted
527
// to a subset of payments by the payments query, containing an offset
528
// index and a maximum number of returned payments.
529
func (d *DB) QueryPayments(query PaymentsQuery) (PaymentsResponse, error) {
78✔
530
        var resp PaymentsResponse
78✔
531

78✔
532
        if err := kvdb.View(d, func(tx kvdb.RTx) error {
156✔
533
                // Get the root payments bucket.
78✔
534
                paymentsBucket := tx.ReadBucket(paymentsRootBucket)
78✔
535
                if paymentsBucket == nil {
120✔
536
                        return nil
42✔
537
                }
42✔
538

539
                // Get the index bucket which maps sequence number -> payment
540
                // hash and duplicate bool. If we have a payments bucket, we
541
                // should have an indexes bucket as well.
542
                indexes := tx.ReadBucket(paymentsIndexBucket)
42✔
543
                if indexes == nil {
42✔
UNCOV
544
                        return fmt.Errorf("index bucket does not exist")
×
UNCOV
545
                }
×
546

547
                // accumulatePayments gets payments with the sequence number
548
                // and hash provided and adds them to our list of payments if
549
                // they meet the criteria of our query. It returns the number
550
                // of payments that were added.
551
                accumulatePayments := func(sequenceKey, hash []byte) (bool,
42✔
552
                        error) {
152✔
553

110✔
554
                        r := bytes.NewReader(hash)
110✔
555
                        paymentHash, err := deserializePaymentIndex(r)
110✔
556
                        if err != nil {
110✔
UNCOV
557
                                return false, err
×
558
                        }
×
559

560
                        payment, err := fetchPaymentWithSequenceNumber(
110✔
561
                                tx, paymentHash, sequenceKey,
110✔
562
                        )
110✔
563
                        if err != nil {
110✔
UNCOV
564
                                return false, err
×
565
                        }
×
566

567
                        // To keep compatibility with the old API, we only
568
                        // return non-succeeded payments if requested.
569
                        if payment.Status != StatusSucceeded &&
110✔
570
                                !query.IncludeIncomplete {
120✔
571

10✔
572
                                return false, err
10✔
573
                        }
10✔
574

575
                        // Get the creation time in Unix seconds, this always
576
                        // rounds down the nanoseconds to full seconds.
577
                        createTime := payment.Info.CreationTime.Unix()
100✔
578

100✔
579
                        // Skip any payments that were created before the
100✔
580
                        // specified time.
100✔
581
                        if createTime < query.CreationDateStart {
124✔
582
                                return false, nil
24✔
583
                        }
24✔
584

585
                        // Skip any payments that were created after the
586
                        // specified time.
587
                        if query.CreationDateEnd != 0 &&
82✔
588
                                createTime > query.CreationDateEnd {
92✔
589

10✔
590
                                return false, nil
10✔
591
                        }
10✔
592

593
                        // At this point, we've exhausted the offset, so we'll
594
                        // begin collecting invoices found within the range.
595
                        resp.Payments = append(resp.Payments, payment)
78✔
596
                        return true, nil
78✔
597
                }
598

599
                // Create a paginator which reads from our sequence index bucket
600
                // with the parameters provided by the payments query.
601
                paginator := newPaginator(
42✔
602
                        indexes.ReadCursor(), query.Reversed, query.IndexOffset,
42✔
603
                        query.MaxPayments,
42✔
604
                )
42✔
605

42✔
606
                // Run a paginated query, adding payments to our response.
42✔
607
                if err := paginator.query(accumulatePayments); err != nil {
42✔
UNCOV
608
                        return err
×
609
                }
×
610

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

×
622
                                return nil
×
623
                        }
×
624

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

UNCOV
637
                        resp.TotalCount = totalPayments
×
638
                }
639

640
                return nil
42✔
641
        }, func() {
78✔
642
                resp = PaymentsResponse{}
78✔
643
        }); err != nil {
78✔
644
                return resp, err
×
645
        }
×
646

647
        // Need to swap the payments slice order if reversed order.
648
        if query.Reversed {
110✔
649
                for l, r := 0, len(resp.Payments)-1; l < r; l, r = l+1, r-1 {
48✔
650
                        resp.Payments[l], resp.Payments[r] =
16✔
651
                                resp.Payments[r], resp.Payments[l]
16✔
652
                }
16✔
653
        }
654

655
        // Set the first and last index of the returned payments so that the
656
        // caller can resume from this point later on.
657
        if len(resp.Payments) > 0 {
116✔
658
                resp.FirstIndexOffset = resp.Payments[0].SequenceNum
38✔
659
                resp.LastIndexOffset =
38✔
660
                        resp.Payments[len(resp.Payments)-1].SequenceNum
38✔
661
        }
38✔
662

663
        return resp, nil
78✔
664
}
665

666
// fetchPaymentWithSequenceNumber get the payment which matches the payment hash
667
// *and* sequence number provided from the database. This is required because
668
// we previously had more than one payment per hash, so we have multiple indexes
669
// pointing to a single payment; we want to retrieve the correct one.
670
func fetchPaymentWithSequenceNumber(tx kvdb.RTx, paymentHash lntypes.Hash,
671
        sequenceNumber []byte) (*MPPayment, error) {
122✔
672

122✔
673
        // We can now lookup the payment keyed by its hash in
122✔
674
        // the payments root bucket.
122✔
675
        bucket, err := fetchPaymentBucket(tx, paymentHash)
122✔
676
        if err != nil {
122✔
UNCOV
677
                return nil, err
×
UNCOV
678
        }
×
679

680
        // A single payment hash can have multiple payments associated with it.
681
        // We lookup our sequence number first, to determine whether this is
682
        // the payment we are actually looking for.
683
        seqBytes := bucket.Get(paymentSequenceKey)
122✔
684
        if seqBytes == nil {
122✔
UNCOV
685
                return nil, ErrNoSequenceNumber
×
UNCOV
686
        }
×
687

688
        // If this top level payment has the sequence number we are looking for,
689
        // return it.
690
        if bytes.Equal(seqBytes, sequenceNumber) {
218✔
691
                return fetchPayment(bucket)
96✔
692
        }
96✔
693

694
        // If we were not looking for the top level payment, we are looking for
695
        // one of our duplicate payments. We need to iterate through the seq
696
        // numbers in this bucket to find the correct payments. If we do not
697
        // find a duplicate payments bucket here, something is wrong.
698
        dup := bucket.NestedReadBucket(duplicatePaymentsBucket)
26✔
699
        if dup == nil {
28✔
700
                return nil, ErrNoDuplicateBucket
2✔
701
        }
2✔
702

703
        var duplicatePayment *MPPayment
24✔
704
        err = dup.ForEach(func(k, v []byte) error {
54✔
705
                subBucket := dup.NestedReadBucket(k)
30✔
706
                if subBucket == nil {
30✔
707
                        // We one bucket for each duplicate to be found.
×
708
                        return ErrNoDuplicateNestedBucket
×
UNCOV
709
                }
×
710

711
                seqBytes := subBucket.Get(duplicatePaymentSequenceKey)
30✔
712
                if seqBytes == nil {
30✔
713
                        return err
×
714
                }
×
715

716
                // If this duplicate payment is not the sequence number we are
717
                // looking for, we can continue.
718
                if !bytes.Equal(seqBytes, sequenceNumber) {
38✔
719
                        return nil
8✔
720
                }
8✔
721

722
                duplicatePayment, err = fetchDuplicatePayment(subBucket)
22✔
723
                if err != nil {
22✔
724
                        return err
×
725
                }
×
726

727
                return nil
22✔
728
        })
729
        if err != nil {
24✔
730
                return nil, err
×
731
        }
×
732

733
        // If none of the duplicate payments matched our sequence number, we
734
        // failed to find the payment with this sequence number; something is
735
        // wrong.
736
        if duplicatePayment == nil {
26✔
737
                return nil, ErrDuplicateNotFound
2✔
738
        }
2✔
739

740
        return duplicatePayment, nil
22✔
741
}
742

743
// DeletePayment deletes a payment from the DB given its payment hash. If
744
// failedHtlcsOnly is set, only failed HTLC attempts of the payment will be
745
// deleted.
746
func (d *DB) DeletePayment(paymentHash lntypes.Hash,
747
        failedHtlcsOnly bool) error {
22✔
748

22✔
749
        return kvdb.Update(d, func(tx kvdb.RwTx) error {
44✔
750
                payments := tx.ReadWriteBucket(paymentsRootBucket)
22✔
751
                if payments == nil {
22✔
752
                        return nil
×
753
                }
×
754

755
                bucket := payments.NestedReadWriteBucket(paymentHash[:])
22✔
756
                if bucket == nil {
24✔
757
                        return fmt.Errorf("non bucket element in payments " +
2✔
758
                                "bucket")
2✔
759
                }
2✔
760

761
                // If the status is InFlight, we cannot safely delete
762
                // the payment information, so we return early.
763
                paymentStatus, err := fetchPaymentStatus(bucket)
20✔
764
                if err != nil {
20✔
765
                        return err
×
766
                }
×
767

768
                // If the payment has inflight HTLCs, we cannot safely delete
769
                // the payment information, so we return an error.
770
                if err := paymentStatus.removable(); err != nil {
26✔
771
                        return fmt.Errorf("payment '%v' has inflight HTLCs"+
6✔
772
                                "and therefore cannot be deleted: %w",
6✔
773
                                paymentHash.String(), err)
6✔
774
                }
6✔
775

776
                // Delete the failed HTLC attempts we found.
777
                if failedHtlcsOnly {
22✔
778
                        toDelete, err := fetchFailedHtlcKeys(bucket)
8✔
779
                        if err != nil {
8✔
780
                                return err
×
781
                        }
×
782

783
                        htlcsBucket := bucket.NestedReadWriteBucket(
8✔
784
                                paymentHtlcsBucket,
8✔
785
                        )
8✔
786

8✔
787
                        for _, htlcID := range toDelete {
20✔
788
                                err = htlcsBucket.Delete(
12✔
789
                                        htlcBucketKey(htlcAttemptInfoKey, htlcID),
12✔
790
                                )
12✔
791
                                if err != nil {
12✔
792
                                        return err
×
793
                                }
×
794

795
                                err = htlcsBucket.Delete(
12✔
796
                                        htlcBucketKey(htlcFailInfoKey, htlcID),
12✔
797
                                )
12✔
798
                                if err != nil {
12✔
799
                                        return err
×
800
                                }
×
801

802
                                err = htlcsBucket.Delete(
12✔
803
                                        htlcBucketKey(htlcSettleInfoKey, htlcID),
12✔
804
                                )
12✔
805
                                if err != nil {
12✔
UNCOV
806
                                        return err
×
807
                                }
×
808
                        }
809

810
                        return nil
8✔
811
                }
812

813
                seqNrs, err := fetchSequenceNumbers(bucket)
6✔
814
                if err != nil {
6✔
UNCOV
815
                        return err
×
816
                }
×
817

818
                if err := payments.DeleteNestedBucket(paymentHash[:]); err != nil {
6✔
819
                        return err
×
820
                }
×
821

822
                indexBucket := tx.ReadWriteBucket(paymentsIndexBucket)
6✔
823
                for _, k := range seqNrs {
12✔
824
                        if err := indexBucket.Delete(k); err != nil {
6✔
UNCOV
825
                                return err
×
UNCOV
826
                        }
×
827
                }
828

829
                return nil
6✔
830
        }, func() {})
22✔
831
}
832

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

846
                var (
18✔
847
                        // deleteBuckets is the set of payment buckets we need
18✔
848
                        // to delete.
18✔
849
                        deleteBuckets [][]byte
18✔
850

18✔
851
                        // deleteIndexes is the set of indexes pointing to these
18✔
852
                        // payments that need to be deleted.
18✔
853
                        deleteIndexes [][]byte
18✔
854

18✔
855
                        // deleteHtlcs maps a payment hash to the HTLC IDs we
18✔
856
                        // want to delete for that payment.
18✔
857
                        deleteHtlcs = make(map[lntypes.Hash][][]byte)
18✔
858
                )
18✔
859
                err := payments.ForEach(func(k, _ []byte) error {
60✔
860
                        bucket := payments.NestedReadBucket(k)
42✔
861
                        if bucket == nil {
42✔
UNCOV
862
                                // We only expect sub-buckets to be found in
×
UNCOV
863
                                // this top-level bucket.
×
UNCOV
864
                                return fmt.Errorf("non bucket element in " +
×
UNCOV
865
                                        "payments bucket")
×
866
                        }
×
867

868
                        // If the status is InFlight, we cannot safely delete
869
                        // the payment information, so we return early.
870
                        paymentStatus, err := fetchPaymentStatus(bucket)
42✔
871
                        if err != nil {
42✔
UNCOV
872
                                return err
×
873
                        }
×
874

875
                        // If the payment has inflight HTLCs, we cannot safely
876
                        // delete the payment information, so we return an nil
877
                        // to skip it.
878
                        if err := paymentStatus.removable(); err != nil {
54✔
879
                                return nil
12✔
880
                        }
12✔
881

882
                        // If we requested to only delete failed payments, we
883
                        // can return if this one is not.
884
                        if failedOnly && paymentStatus != StatusFailed {
38✔
885
                                return nil
8✔
886
                        }
8✔
887

888
                        // If we are only deleting failed HTLCs, fetch them.
889
                        if failedHtlcsOnly {
28✔
890
                                toDelete, err := fetchFailedHtlcKeys(bucket)
6✔
891
                                if err != nil {
6✔
892
                                        return err
×
UNCOV
893
                                }
×
894

895
                                hash, err := lntypes.MakeHash(k)
6✔
896
                                if err != nil {
6✔
897
                                        return err
×
UNCOV
898
                                }
×
899

900
                                deleteHtlcs[hash] = toDelete
6✔
901

6✔
902
                                // We return, we are only deleting attempts.
6✔
903
                                return nil
6✔
904
                        }
905

906
                        // Add the bucket to the set of buckets we can delete.
907
                        deleteBuckets = append(deleteBuckets, k)
16✔
908

16✔
909
                        // Get all the sequence number associated with the
16✔
910
                        // payment, including duplicates.
16✔
911
                        seqNrs, err := fetchSequenceNumbers(bucket)
16✔
912
                        if err != nil {
16✔
UNCOV
913
                                return err
×
UNCOV
914
                        }
×
915

916
                        deleteIndexes = append(deleteIndexes, seqNrs...)
16✔
917
                        numPayments++
16✔
918
                        return nil
16✔
919
                })
920
                if err != nil {
18✔
921
                        return err
×
922
                }
×
923

924
                // Delete the failed HTLC attempts we found.
925
                for hash, htlcIDs := range deleteHtlcs {
24✔
926
                        bucket := payments.NestedReadWriteBucket(hash[:])
6✔
927
                        htlcsBucket := bucket.NestedReadWriteBucket(
6✔
928
                                paymentHtlcsBucket,
6✔
929
                        )
6✔
930

6✔
931
                        for _, aid := range htlcIDs {
12✔
932
                                if err := htlcsBucket.Delete(
6✔
933
                                        htlcBucketKey(htlcAttemptInfoKey, aid),
6✔
934
                                ); err != nil {
6✔
935
                                        return err
×
936
                                }
×
937

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

944
                                if err := htlcsBucket.Delete(
6✔
945
                                        htlcBucketKey(htlcSettleInfoKey, aid),
6✔
946
                                ); err != nil {
6✔
UNCOV
947
                                        return err
×
948
                                }
×
949
                        }
950
                }
951

952
                for _, k := range deleteBuckets {
34✔
953
                        if err := payments.DeleteNestedBucket(k); err != nil {
16✔
UNCOV
954
                                return err
×
UNCOV
955
                        }
×
956
                }
957

958
                // Get our index bucket and delete all indexes pointing to the
959
                // payments we are deleting.
960
                indexBucket := tx.ReadWriteBucket(paymentsIndexBucket)
18✔
961
                for _, k := range deleteIndexes {
36✔
962
                        if err := indexBucket.Delete(k); err != nil {
18✔
UNCOV
963
                                return err
×
UNCOV
964
                        }
×
965
                }
966

967
                return nil
18✔
968
        }, func() {
18✔
969
                numPayments = 0
18✔
970
        })
18✔
971
        if err != nil {
18✔
UNCOV
972
                return 0, err
×
UNCOV
973
        }
×
974

975
        return numPayments, nil
18✔
976
}
977

978
// fetchSequenceNumbers fetches all the sequence numbers associated with a
979
// payment, including those belonging to any duplicate payments.
980
func fetchSequenceNumbers(paymentBucket kvdb.RBucket) ([][]byte, error) {
22✔
981
        seqNum := paymentBucket.Get(paymentSequenceKey)
22✔
982
        if seqNum == nil {
22✔
UNCOV
983
                return nil, errors.New("expected sequence number")
×
UNCOV
984
        }
×
985

986
        sequenceNumbers := [][]byte{seqNum}
22✔
987

22✔
988
        // Get the duplicate payments bucket, if it has no duplicates, just
22✔
989
        // return early with the payment sequence number.
22✔
990
        duplicates := paymentBucket.NestedReadBucket(duplicatePaymentsBucket)
22✔
991
        if duplicates == nil {
42✔
992
                return sequenceNumbers, nil
20✔
993
        }
20✔
994

995
        // If we do have duplicated, they are keyed by sequence number, so we
996
        // iterate through the duplicates bucket and add them to our set of
997
        // sequence numbers.
998
        if err := duplicates.ForEach(func(k, v []byte) error {
4✔
999
                sequenceNumbers = append(sequenceNumbers, k)
2✔
1000
                return nil
2✔
1001
        }); err != nil {
2✔
UNCOV
1002
                return nil, err
×
UNCOV
1003
        }
×
1004

1005
        return sequenceNumbers, nil
2✔
1006
}
1007

1008
// nolint: dupl
1009
func serializePaymentCreationInfo(w io.Writer, c *PaymentCreationInfo) error {
308✔
1010
        var scratch [8]byte
308✔
1011

308✔
1012
        if _, err := w.Write(c.PaymentIdentifier[:]); err != nil {
308✔
1013
                return err
×
UNCOV
1014
        }
×
1015

1016
        byteOrder.PutUint64(scratch[:], uint64(c.Value))
308✔
1017
        if _, err := w.Write(scratch[:]); err != nil {
308✔
UNCOV
1018
                return err
×
UNCOV
1019
        }
×
1020

1021
        if err := serializeTime(w, c.CreationTime); err != nil {
308✔
1022
                return err
×
UNCOV
1023
        }
×
1024

1025
        byteOrder.PutUint32(scratch[:4], uint32(len(c.PaymentRequest)))
308✔
1026
        if _, err := w.Write(scratch[:4]); err != nil {
308✔
UNCOV
1027
                return err
×
UNCOV
1028
        }
×
1029

1030
        if _, err := w.Write(c.PaymentRequest[:]); err != nil {
308✔
UNCOV
1031
                return err
×
UNCOV
1032
        }
×
1033

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

1043
        return nil
308✔
1044
}
1045

1046
func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo,
1047
        error) {
1,290✔
1048

1,290✔
1049
        var scratch [8]byte
1,290✔
1050

1,290✔
1051
        c := &PaymentCreationInfo{}
1,290✔
1052

1,290✔
1053
        if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil {
1,290✔
UNCOV
1054
                return nil, err
×
UNCOV
1055
        }
×
1056

1057
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
1,290✔
1058
                return nil, err
×
1059
        }
×
1060
        c.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
1,290✔
1061

1,290✔
1062
        creationTime, err := deserializeTime(r)
1,290✔
1063
        if err != nil {
1,290✔
1064
                return nil, err
×
UNCOV
1065
        }
×
1066
        c.CreationTime = creationTime
1,290✔
1067

1,290✔
1068
        if _, err := io.ReadFull(r, scratch[:4]); err != nil {
1,290✔
UNCOV
1069
                return nil, err
×
1070
        }
×
1071

1072
        reqLen := uint32(byteOrder.Uint32(scratch[:4]))
1,290✔
1073
        payReq := make([]byte, reqLen)
1,290✔
1074
        if reqLen > 0 {
2,580✔
1075
                if _, err := io.ReadFull(r, payReq); err != nil {
1,290✔
UNCOV
1076
                        return nil, err
×
UNCOV
1077
                }
×
1078
        }
1079
        c.PaymentRequest = payReq
1,290✔
1080

1,290✔
1081
        // Any remaining bytes are TLV encoded records. Currently, these are
1,290✔
1082
        // only the custom records provided by the user to be sent to the first
1,290✔
1083
        // hop. But this can easily be extended with further records by merging
1,290✔
1084
        // the records into a single TLV stream.
1,290✔
1085
        c.FirstHopCustomRecords, err = lnwire.ParseCustomRecordsFrom(r)
1,290✔
1086
        if err != nil {
1,290✔
UNCOV
1087
                return nil, err
×
UNCOV
1088
        }
×
1089

1090
        return c, nil
1,290✔
1091
}
1092

1093
func serializeHTLCAttemptInfo(w io.Writer, a *HTLCAttemptInfo) error {
150✔
1094
        if err := WriteElements(w, a.sessionKey); err != nil {
150✔
UNCOV
1095
                return err
×
UNCOV
1096
        }
×
1097

1098
        if err := SerializeRoute(w, a.Route); err != nil {
150✔
UNCOV
1099
                return err
×
UNCOV
1100
        }
×
1101

1102
        if err := serializeTime(w, a.AttemptTime); err != nil {
150✔
1103
                return err
×
UNCOV
1104
        }
×
1105

1106
        // If the hash is nil we can just return.
1107
        if a.Hash == nil {
150✔
UNCOV
1108
                return nil
×
UNCOV
1109
        }
×
1110

1111
        if _, err := w.Write(a.Hash[:]); err != nil {
150✔
UNCOV
1112
                return err
×
UNCOV
1113
        }
×
1114

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

1130
        if _, err := w.Write(tlvData); err != nil {
150✔
UNCOV
1131
                return err
×
UNCOV
1132
        }
×
1133

1134
        return nil
150✔
1135
}
1136

1137
func deserializeHTLCAttemptInfo(r io.Reader) (*HTLCAttemptInfo, error) {
1,412✔
1138
        a := &HTLCAttemptInfo{}
1,412✔
1139
        err := ReadElements(r, &a.sessionKey)
1,412✔
1140
        if err != nil {
1,412✔
1141
                return nil, err
×
UNCOV
1142
        }
×
1143

1144
        a.Route, err = DeserializeRoute(r)
1,412✔
1145
        if err != nil {
1,412✔
1146
                return nil, err
×
UNCOV
1147
        }
×
1148

1149
        a.AttemptTime, err = deserializeTime(r)
1,412✔
1150
        if err != nil {
1,412✔
UNCOV
1151
                return nil, err
×
UNCOV
1152
        }
×
1153

1154
        hash := lntypes.Hash{}
1,412✔
1155
        _, err = io.ReadFull(r, hash[:])
1,412✔
1156

1,412✔
1157
        switch {
1,412✔
1158
        // Older payment attempts wouldn't have the hash set, in which case we
1159
        // can just return.
UNCOV
1160
        case err == io.EOF, err == io.ErrUnexpectedEOF:
×
UNCOV
1161
                return a, nil
×
1162

UNCOV
1163
        case err != nil:
×
UNCOV
1164
                return nil, err
×
1165

1166
        default:
1,412✔
1167
        }
1168

1169
        a.Hash = &hash
1,412✔
1170

1,412✔
1171
        // Read any remaining data (if any) and parse it into the known records
1,412✔
1172
        // and custom records.
1,412✔
1173
        extraData, err := io.ReadAll(r)
1,412✔
1174
        if err != nil {
1,412✔
UNCOV
1175
                return nil, err
×
1176
        }
×
1177

1178
        customRecords, _, _, err := lnwire.ParseAndExtractCustomRecords(
1,412✔
1179
                extraData, &a.Route.FirstHopAmount,
1,412✔
1180
        )
1,412✔
1181
        if err != nil {
1,412✔
UNCOV
1182
                return nil, err
×
UNCOV
1183
        }
×
1184

1185
        a.Route.FirstHopWireCustomRecords = customRecords
1,412✔
1186

1,412✔
1187
        return a, nil
1,412✔
1188
}
1189

1190
func serializeHop(w io.Writer, h *route.Hop) error {
304✔
1191
        if err := WriteElements(w,
304✔
1192
                h.PubKeyBytes[:],
304✔
1193
                h.ChannelID,
304✔
1194
                h.OutgoingTimeLock,
304✔
1195
                h.AmtToForward,
304✔
1196
        ); err != nil {
304✔
UNCOV
1197
                return err
×
UNCOV
1198
        }
×
1199

1200
        if err := binary.Write(w, byteOrder, h.LegacyPayload); err != nil {
304✔
UNCOV
1201
                return err
×
1202
        }
×
1203

1204
        // For legacy payloads, we don't need to write any TLV records, so
1205
        // we'll write a zero indicating the our serialized TLV map has no
1206
        // records.
1207
        if h.LegacyPayload {
464✔
1208
                return WriteElements(w, uint32(0))
160✔
1209
        }
160✔
1210

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

1223
        // Add blinding point and encrypted data if present.
1224
        if h.EncryptedData != nil {
154✔
1225
                records = append(records, record.NewEncryptedDataRecord(
10✔
1226
                        &h.EncryptedData,
10✔
1227
                ))
10✔
1228
        }
10✔
1229

1230
        if h.BlindingPoint != nil {
152✔
1231
                records = append(records, record.NewBlindingPointRecord(
8✔
1232
                        &h.BlindingPoint,
8✔
1233
                ))
8✔
1234
        }
8✔
1235

1236
        if h.AMP != nil {
150✔
1237
                records = append(records, h.AMP.Record())
6✔
1238
        }
6✔
1239

1240
        if h.Metadata != nil {
278✔
1241
                records = append(records, record.NewMetadataRecord(&h.Metadata))
134✔
1242
        }
134✔
1243

1244
        if h.TotalAmtMsat != 0 {
152✔
1245
                totalMsatInt := uint64(h.TotalAmtMsat)
8✔
1246
                records = append(
8✔
1247
                        records, record.NewTotalAmtMsatBlinded(&totalMsatInt),
8✔
1248
                )
8✔
1249
        }
8✔
1250

1251
        // Final sanity check to absolutely rule out custom records that are not
1252
        // custom and write into the standard range.
1253
        if err := h.CustomRecords.Validate(); err != nil {
144✔
UNCOV
1254
                return err
×
UNCOV
1255
        }
×
1256

1257
        // Convert custom records to tlv and add to the record list.
1258
        // MapToRecords sorts the list, so adding it here will keep the list
1259
        // canonical.
1260
        tlvRecords := tlv.MapToRecords(h.CustomRecords)
144✔
1261
        records = append(records, tlvRecords...)
144✔
1262

144✔
1263
        // Otherwise, we'll transform our slice of records into a map of the
144✔
1264
        // raw bytes, then serialize them in-line with a length (number of
144✔
1265
        // elements) prefix.
144✔
1266
        mapRecords, err := tlv.RecordsToMap(records)
144✔
1267
        if err != nil {
144✔
1268
                return err
×
UNCOV
1269
        }
×
1270

1271
        numRecords := uint32(len(mapRecords))
144✔
1272
        if err := WriteElements(w, numRecords); err != nil {
144✔
1273
                return err
×
UNCOV
1274
        }
×
1275

1276
        for recordType, rawBytes := range mapRecords {
684✔
1277
                if err := WriteElements(w, recordType); err != nil {
540✔
UNCOV
1278
                        return err
×
UNCOV
1279
                }
×
1280

1281
                if err := wire.WriteVarBytes(w, 0, rawBytes); err != nil {
540✔
UNCOV
1282
                        return err
×
UNCOV
1283
                }
×
1284
        }
1285

1286
        return nil
144✔
1287
}
1288

1289
// maxOnionPayloadSize is the largest Sphinx payload possible, so we don't need
1290
// to read/write a TLV stream larger than this.
1291
const maxOnionPayloadSize = 1300
1292

1293
func deserializeHop(r io.Reader) (*route.Hop, error) {
2,828✔
1294
        h := &route.Hop{}
2,828✔
1295

2,828✔
1296
        var pub []byte
2,828✔
1297
        if err := ReadElements(r, &pub); err != nil {
2,828✔
UNCOV
1298
                return nil, err
×
1299
        }
×
1300
        copy(h.PubKeyBytes[:], pub)
2,828✔
1301

2,828✔
1302
        if err := ReadElements(r,
2,828✔
1303
                &h.ChannelID, &h.OutgoingTimeLock, &h.AmtToForward,
2,828✔
1304
        ); err != nil {
2,828✔
UNCOV
1305
                return nil, err
×
1306
        }
×
1307

1308
        // TODO(roasbeef): change field to allow LegacyPayload false to be the
1309
        // legacy default?
1310
        err := binary.Read(r, byteOrder, &h.LegacyPayload)
2,828✔
1311
        if err != nil {
2,828✔
1312
                return nil, err
×
UNCOV
1313
        }
×
1314

1315
        var numElements uint32
2,828✔
1316
        if err := ReadElements(r, &numElements); err != nil {
2,828✔
UNCOV
1317
                return nil, err
×
UNCOV
1318
        }
×
1319

1320
        // If there're no elements, then we can return early.
1321
        if numElements == 0 {
4,298✔
1322
                return h, nil
1,470✔
1323
        }
1,470✔
1324

1325
        tlvMap := make(map[uint64][]byte)
1,364✔
1326
        for i := uint32(0); i < numElements; i++ {
6,782✔
1327
                var tlvType uint64
5,418✔
1328
                if err := ReadElements(r, &tlvType); err != nil {
5,418✔
UNCOV
1329
                        return nil, err
×
1330
                }
×
1331

1332
                rawRecordBytes, err := wire.ReadVarBytes(
5,418✔
1333
                        r, 0, maxOnionPayloadSize, "tlv",
5,418✔
1334
                )
5,418✔
1335
                if err != nil {
5,418✔
UNCOV
1336
                        return nil, err
×
UNCOV
1337
                }
×
1338

1339
                tlvMap[tlvType] = rawRecordBytes
5,418✔
1340
        }
1341

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

1,352✔
1353
                var (
1,352✔
1354
                        mpp    = &record.MPP{}
1,352✔
1355
                        mppRec = mpp.Record()
1,352✔
1356
                        r      = bytes.NewReader(mppBytes)
1,352✔
1357
                )
1,352✔
1358
                err := mppRec.Decode(r, uint64(len(mppBytes)))
1,352✔
1359
                if err != nil {
1,352✔
UNCOV
1360
                        return nil, err
×
UNCOV
1361
                }
×
1362
                h.MPP = mpp
1,352✔
1363
        }
1364

1365
        // If encrypted data or blinding key are present, remove them from
1366
        // the TLV map and parse into proper types.
1367
        encryptedDataType := uint64(record.EncryptedDataOnionType)
1,364✔
1368
        if data, ok := tlvMap[encryptedDataType]; ok {
1,374✔
1369
                delete(tlvMap, encryptedDataType)
10✔
1370
                h.EncryptedData = data
10✔
1371
        }
10✔
1372

1373
        blindingType := uint64(record.BlindingPointOnionType)
1,364✔
1374
        if blindingPoint, ok := tlvMap[blindingType]; ok {
1,372✔
1375
                delete(tlvMap, blindingType)
8✔
1376

8✔
1377
                h.BlindingPoint, err = btcec.ParsePubKey(blindingPoint)
8✔
1378
                if err != nil {
8✔
UNCOV
1379
                        return nil, fmt.Errorf("invalid blinding point: %w",
×
UNCOV
1380
                                err)
×
UNCOV
1381
                }
×
1382
        }
1383

1384
        ampType := uint64(record.AMPOnionType)
1,364✔
1385
        if ampBytes, ok := tlvMap[ampType]; ok {
1,370✔
1386
                delete(tlvMap, ampType)
6✔
1387

6✔
1388
                var (
6✔
1389
                        amp    = &record.AMP{}
6✔
1390
                        ampRec = amp.Record()
6✔
1391
                        r      = bytes.NewReader(ampBytes)
6✔
1392
                )
6✔
1393
                err := ampRec.Decode(r, uint64(len(ampBytes)))
6✔
1394
                if err != nil {
6✔
UNCOV
1395
                        return nil, err
×
UNCOV
1396
                }
×
1397
                h.AMP = amp
6✔
1398
        }
1399

1400
        // If the metadata type is present, remove it from the tlv map and
1401
        // populate directly on the hop.
1402
        metadataType := uint64(record.MetadataOnionType)
1,364✔
1403
        if metadata, ok := tlvMap[metadataType]; ok {
2,718✔
1404
                delete(tlvMap, metadataType)
1,354✔
1405

1,354✔
1406
                h.Metadata = metadata
1,354✔
1407
        }
1,354✔
1408

1409
        totalAmtMsatType := uint64(record.TotalAmtMsatBlindedType)
1,364✔
1410
        if totalAmtMsat, ok := tlvMap[totalAmtMsatType]; ok {
1,372✔
1411
                delete(tlvMap, totalAmtMsatType)
8✔
1412

8✔
1413
                var (
8✔
1414
                        totalAmtMsatInt uint64
8✔
1415
                        buf             [8]byte
8✔
1416
                )
8✔
1417
                if err := tlv.DTUint64(
8✔
1418
                        bytes.NewReader(totalAmtMsat),
8✔
1419
                        &totalAmtMsatInt,
8✔
1420
                        &buf,
8✔
1421
                        uint64(len(totalAmtMsat)),
8✔
1422
                ); err != nil {
8✔
UNCOV
1423
                        return nil, err
×
UNCOV
1424
                }
×
1425

1426
                h.TotalAmtMsat = lnwire.MilliSatoshi(totalAmtMsatInt)
8✔
1427
        }
1428

1429
        h.CustomRecords = tlvMap
1,364✔
1430

1,364✔
1431
        return h, nil
1,364✔
1432
}
1433

1434
// SerializeRoute serializes a route.
1435
func SerializeRoute(w io.Writer, r route.Route) error {
154✔
1436
        if err := WriteElements(w,
154✔
1437
                r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
154✔
1438
        ); err != nil {
154✔
UNCOV
1439
                return err
×
UNCOV
1440
        }
×
1441

1442
        if err := WriteElements(w, uint32(len(r.Hops))); err != nil {
154✔
1443
                return err
×
UNCOV
1444
        }
×
1445

1446
        for _, h := range r.Hops {
458✔
1447
                if err := serializeHop(w, h); err != nil {
304✔
UNCOV
1448
                        return err
×
UNCOV
1449
                }
×
1450
        }
1451

1452
        // Any new/extra TLV data is encoded in serializeHTLCAttemptInfo!
1453

1454
        return nil
154✔
1455
}
1456

1457
// DeserializeRoute deserializes a route.
1458
func DeserializeRoute(r io.Reader) (route.Route, error) {
1,416✔
1459
        rt := route.Route{}
1,416✔
1460
        if err := ReadElements(r,
1,416✔
1461
                &rt.TotalTimeLock, &rt.TotalAmount,
1,416✔
1462
        ); err != nil {
1,416✔
1463
                return rt, err
×
UNCOV
1464
        }
×
1465

1466
        var pub []byte
1,416✔
1467
        if err := ReadElements(r, &pub); err != nil {
1,416✔
1468
                return rt, err
×
1469
        }
×
1470
        copy(rt.SourcePubKey[:], pub)
1,416✔
1471

1,416✔
1472
        var numHops uint32
1,416✔
1473
        if err := ReadElements(r, &numHops); err != nil {
1,416✔
UNCOV
1474
                return rt, err
×
1475
        }
×
1476

1477
        var hops []*route.Hop
1,416✔
1478
        for i := uint32(0); i < numHops; i++ {
4,244✔
1479
                hop, err := deserializeHop(r)
2,828✔
1480
                if err != nil {
2,828✔
UNCOV
1481
                        return rt, err
×
UNCOV
1482
                }
×
1483
                hops = append(hops, hop)
2,828✔
1484
        }
1485
        rt.Hops = hops
1,416✔
1486

1,416✔
1487
        // Any new/extra TLV data is decoded in deserializeHTLCAttemptInfo!
1,416✔
1488

1,416✔
1489
        return rt, nil
1,416✔
1490
}
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