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

lightningnetwork / lnd / 15117244231

19 May 2025 03:37PM UTC coverage: 57.988% (-11.0%) from 68.99%
15117244231

Pull #9825

github

web-flow
Merge e714b31b8 into 3707b1fb7
Pull Request #9825: Refactor Payment PR 1

326 of 469 new or added lines in 4 files covered. (69.51%)

29180 existing lines in 455 files now uncovered.

96428 of 166290 relevant lines covered (57.99%)

1.22 hits per line

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

59.18
/channeldb/payments.go
1
package channeldb
2

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

127
// Payment operations related errors.
128
var (
129
        // ErrAlreadyPaid signals we have already paid this payment hash.
130
        ErrAlreadyPaid = errors.New("invoice is already paid")
131

132
        // ErrPaymentInFlight signals that payment for this payment hash is
133
        // already "in flight" on the network.
134
        ErrPaymentInFlight = errors.New("payment is in transition")
135

136
        // ErrPaymentExists is returned when we try to initialize an already
137
        // existing payment that is not failed.
138
        ErrPaymentExists = errors.New("payment already exists")
139

140
        // ErrPaymentInternal is returned when performing the payment has a
141
        // conflicting state, such as,
142
        // - payment has StatusSucceeded but remaining amount is not zero.
143
        // - payment has StatusInitiated but remaining amount is zero.
144
        // - payment has StatusFailed but remaining amount is zero.
145
        ErrPaymentInternal = errors.New("internal error")
146

147
        // ErrPaymentNotInitiated is returned if the payment wasn't initiated.
148
        ErrPaymentNotInitiated = errors.New("payment isn't initiated")
149

150
        // ErrPaymentAlreadySucceeded is returned in the event we attempt to
151
        // change the status of a payment already succeeded.
152
        ErrPaymentAlreadySucceeded = errors.New("payment is already succeeded")
153

154
        // ErrPaymentAlreadyFailed is returned in the event we attempt to alter
155
        // a failed payment.
156
        ErrPaymentAlreadyFailed = errors.New("payment has already failed")
157

158
        // ErrUnknownPaymentStatus is returned when we do not recognize the
159
        // existing state of a payment.
160
        ErrUnknownPaymentStatus = errors.New("unknown payment status")
161

162
        // ErrPaymentTerminal is returned if we attempt to alter a payment that
163
        // already has reached a terminal condition.
164
        ErrPaymentTerminal = errors.New("payment has reached terminal " +
165
                "condition")
166

167
        // ErrAttemptAlreadySettled is returned if we try to alter an already
168
        // settled HTLC attempt.
169
        ErrAttemptAlreadySettled = errors.New("attempt already settled")
170

171
        // ErrAttemptAlreadyFailed is returned if we try to alter an already
172
        // failed HTLC attempt.
173
        ErrAttemptAlreadyFailed = errors.New("attempt already failed")
174

175
        // ErrValueMismatch is returned if we try to register a non-MPP attempt
176
        // with an amount that doesn't match the payment amount.
177
        ErrValueMismatch = errors.New("attempted value doesn't match payment " +
178
                "amount")
179

180
        // ErrValueExceedsAmt is returned if we try to register an attempt that
181
        // would take the total sent amount above the payment amount.
182
        ErrValueExceedsAmt = errors.New("attempted value exceeds payment " +
183
                "amount")
184

185
        // ErrNonMPPayment is returned if we try to register an MPP attempt for
186
        // a payment that already has a non-MPP attempt registered.
187
        ErrNonMPPayment = errors.New("payment has non-MPP attempts")
188

189
        // ErrMPPayment is returned if we try to register a non-MPP attempt for
190
        // a payment that already has an MPP attempt registered.
191
        ErrMPPayment = errors.New("payment has MPP attempts")
192

193
        // ErrMPPRecordInBlindedPayment is returned if we try to register an
194
        // attempt with an MPP record for a payment to a blinded path.
195
        ErrMPPRecordInBlindedPayment = errors.New("blinded payment cannot " +
196
                "contain MPP records")
197

198
        // ErrBlindedPaymentTotalAmountMismatch is returned if we try to
199
        // register an HTLC shard to a blinded route where the total amount
200
        // doesn't match existing shards.
201
        ErrBlindedPaymentTotalAmountMismatch = errors.New("blinded path " +
202
                "total amount mismatch")
203

204
        // ErrMPPPaymentAddrMismatch is returned if we try to register an MPP
205
        // shard where the payment address doesn't match existing shards.
206
        ErrMPPPaymentAddrMismatch = errors.New("payment address mismatch")
207

208
        // ErrMPPTotalAmountMismatch is returned if we try to register an MPP
209
        // shard where the total amount doesn't match existing shards.
210
        ErrMPPTotalAmountMismatch = errors.New("mp payment total amount " +
211
                "mismatch")
212

213
        // ErrPaymentPendingSettled is returned when we try to add a new
214
        // attempt to a payment that has at least one of its HTLCs settled.
215
        ErrPaymentPendingSettled = errors.New("payment has settled htlcs")
216

217
        // ErrPaymentPendingFailed is returned when we try to add a new attempt
218
        // to a payment that already has a failure reason.
219
        ErrPaymentPendingFailed = errors.New("payment has failure reason")
220

221
        // ErrSentExceedsTotal is returned if the payment's current total sent
222
        // amount exceed the total amount.
223
        ErrSentExceedsTotal = errors.New("total sent exceeds total amount")
224

225
        // errNoAttemptInfo is returned when no attempt info is stored yet.
226
        errNoAttemptInfo = errors.New("unable to find attempt info for " +
227
                "inflight payment")
228

229
        // errNoSequenceNrIndex is returned when an attempt to lookup a payment
230
        // index is made for a sequence number that is not indexed.
231
        errNoSequenceNrIndex = errors.New("payment sequence number index " +
232
                "does not exist")
233
)
234

235
// Payment operations related constants.
236
const (
237
        // paymentSeqBlockSize is the block size used when we batch allocate
238
        // payment sequences for future payments.
239
        paymentSeqBlockSize = 1000
240

241
        // paymentProgressLogInterval is the interval we use limiting the
242
        // logging output of payment processing.
243
        paymentProgressLogInterval = 30 * time.Second
244
)
245

246
// FailureReason encodes the reason a payment ultimately failed.
247
type FailureReason byte
248

249
const (
250
        // FailureReasonTimeout indicates that the payment did timeout before a
251
        // successful payment attempt was made.
252
        FailureReasonTimeout FailureReason = 0
253

254
        // FailureReasonNoRoute indicates no successful route to the
255
        // destination was found during path finding.
256
        FailureReasonNoRoute FailureReason = 1
257

258
        // FailureReasonError indicates that an unexpected error happened during
259
        // payment.
260
        FailureReasonError FailureReason = 2
261

262
        // FailureReasonPaymentDetails indicates that either the hash is unknown
263
        // or the final cltv delta or amount is incorrect.
264
        FailureReasonPaymentDetails FailureReason = 3
265

266
        // FailureReasonInsufficientBalance indicates that we didn't have enough
267
        // balance to complete the payment.
268
        FailureReasonInsufficientBalance FailureReason = 4
269

270
        // FailureReasonCanceled indicates that the payment was canceled by the
271
        // user.
272
        FailureReasonCanceled FailureReason = 5
273

274
        // TODO(joostjager): Add failure reasons for:
275
        // LocalLiquidityInsufficient, RemoteCapacityInsufficient.
276
)
277

278
// Error returns a human-readable error string for the FailureReason.
279
func (r FailureReason) Error() string {
2✔
280
        return r.String()
2✔
281
}
2✔
282

283
// String returns a human-readable FailureReason.
284
func (r FailureReason) String() string {
2✔
285
        switch r {
2✔
UNCOV
286
        case FailureReasonTimeout:
×
UNCOV
287
                return "timeout"
×
288
        case FailureReasonNoRoute:
2✔
289
                return "no_route"
2✔
UNCOV
290
        case FailureReasonError:
×
UNCOV
291
                return "error"
×
292
        case FailureReasonPaymentDetails:
2✔
293
                return "incorrect_payment_details"
2✔
294
        case FailureReasonInsufficientBalance:
2✔
295
                return "insufficient_balance"
2✔
UNCOV
296
        case FailureReasonCanceled:
×
UNCOV
297
                return "canceled"
×
298
        }
299

300
        return "unknown"
×
301
}
302

303
// KVPaymentsDB implements persistence for payments and payment attempts.
304
type KVPaymentsDB struct {
305
        paymentSeqMx     sync.Mutex
306
        currPaymentSeq   uint64
307
        storedPaymentSeq uint64
308
        db               *DB
309
}
310

311
// NewKVPaymentsDB creates a new instance of the KVPaymentsDB.
312
func NewKVPaymentsDB(db *DB) *KVPaymentsDB {
2✔
313
        return &KVPaymentsDB{
2✔
314
                db: db,
2✔
315
        }
2✔
316
}
2✔
317

318
// InitPayment checks or records the given PaymentCreationInfo with the DB,
319
// making sure it does not already exist as an in-flight payment. When this
320
// method returns successfully, the payment is guaranteed to be in the InFlight
321
// state.
322
func (p *KVPaymentsDB) InitPayment(paymentHash lntypes.Hash,
323
        info *PaymentCreationInfo) error {
2✔
324

2✔
325
        // Obtain a new sequence number for this payment. This is used
2✔
326
        // to sort the payments in order of creation, and also acts as
2✔
327
        // a unique identifier for each payment.
2✔
328
        sequenceNum, err := p.nextPaymentSequence()
2✔
329
        if err != nil {
2✔
NEW
330
                return err
×
NEW
331
        }
×
332

333
        var b bytes.Buffer
2✔
334
        if err := serializePaymentCreationInfo(&b, info); err != nil {
2✔
NEW
335
                return err
×
NEW
336
        }
×
337
        infoBytes := b.Bytes()
2✔
338

2✔
339
        var updateErr error
2✔
340
        err = kvdb.Batch(p.db.Backend, func(tx kvdb.RwTx) error {
4✔
341
                // Reset the update error, to avoid carrying over an error
2✔
342
                // from a previous execution of the batched db transaction.
2✔
343
                updateErr = nil
2✔
344

2✔
345
                prefetchPayment(tx, paymentHash)
2✔
346
                bucket, err := createPaymentBucket(tx, paymentHash)
2✔
347
                if err != nil {
2✔
NEW
348
                        return err
×
NEW
349
                }
×
350

351
                // Get the existing status of this payment, if any.
352
                paymentStatus, err := fetchPaymentStatus(bucket)
2✔
353

2✔
354
                switch {
2✔
355
                // If no error is returned, it means we already have this
356
                // payment. We'll check the status to decide whether we allow
357
                // retrying the payment or return a specific error.
358
                case err == nil:
2✔
359
                        if err := paymentStatus.initializable(); err != nil {
4✔
360
                                updateErr = err
2✔
361
                                return nil
2✔
362
                        }
2✔
363

364
                // Otherwise, if the error is not `ErrPaymentNotInitiated`,
365
                // we'll return the error.
NEW
366
                case !errors.Is(err, ErrPaymentNotInitiated):
×
NEW
367
                        return err
×
368
                }
369

370
                // Before we set our new sequence number, we check whether this
371
                // payment has a previously set sequence number and remove its
372
                // index entry if it exists. This happens in the case where we
373
                // have a previously attempted payment which was left in a state
374
                // where we can retry.
375
                seqBytes := bucket.Get(paymentSequenceKey)
2✔
376
                if seqBytes != nil {
4✔
377
                        indexBucket := tx.ReadWriteBucket(paymentsIndexBucket)
2✔
378
                        if err := indexBucket.Delete(seqBytes); err != nil {
2✔
NEW
379
                                return err
×
NEW
380
                        }
×
381
                }
382

383
                // Once we have obtained a sequence number, we add an entry
384
                // to our index bucket which will map the sequence number to
385
                // our payment identifier.
386
                err = createPaymentIndexEntry(
2✔
387
                        tx, sequenceNum, info.PaymentIdentifier,
2✔
388
                )
2✔
389
                if err != nil {
2✔
NEW
390
                        return err
×
NEW
391
                }
×
392

393
                err = bucket.Put(paymentSequenceKey, sequenceNum)
2✔
394
                if err != nil {
2✔
NEW
395
                        return err
×
NEW
396
                }
×
397

398
                // Add the payment info to the bucket, which contains the
399
                // static information for this payment
400
                err = bucket.Put(paymentCreationInfoKey, infoBytes)
2✔
401
                if err != nil {
2✔
NEW
402
                        return err
×
NEW
403
                }
×
404

405
                // We'll delete any lingering HTLCs to start with, in case we
406
                // are initializing a payment that was attempted earlier, but
407
                // left in a state where we could retry.
408
                err = bucket.DeleteNestedBucket(paymentHtlcsBucket)
2✔
409
                if err != nil && err != kvdb.ErrBucketNotFound {
2✔
NEW
410
                        return err
×
NEW
411
                }
×
412

413
                // Also delete any lingering failure info now that we are
414
                // re-attempting.
415
                return bucket.Delete(paymentFailInfoKey)
2✔
416
        })
417
        if err != nil {
2✔
NEW
418
                return fmt.Errorf("unable to init payment: %w", err)
×
NEW
419
        }
×
420

421
        return updateErr
2✔
422
}
423

424
// DeleteFailedAttempts deletes all failed htlcs for a payment if configured
425
// by the KVPaymentsDB db.
426
func (p *KVPaymentsDB) DeleteFailedAttempts(hash lntypes.Hash) error {
2✔
427
        if !p.db.keepFailedPaymentAttempts {
2✔
NEW
428
                const failedHtlcsOnly = true
×
NEW
429
                err := p.db.DeletePayment(hash, failedHtlcsOnly)
×
NEW
430
                if err != nil {
×
NEW
431
                        return err
×
NEW
432
                }
×
433
        }
434

435
        return nil
2✔
436
}
437

438
// paymentIndexTypeHash is a payment index type which indicates that we have
439
// created an index of payment sequence number to payment hash.
440
type paymentIndexType uint8
441

442
// paymentIndexTypeHash is a payment index type which indicates that we have
443
// created an index of payment sequence number to payment hash.
444
const paymentIndexTypeHash paymentIndexType = 0
445

446
// createPaymentIndexEntry creates a payment hash typed index for a payment. The
447
// index produced contains a payment index type (which can be used in future to
448
// signal different payment index types) and the payment identifier.
449
func createPaymentIndexEntry(tx kvdb.RwTx, sequenceNumber []byte,
450
        id lntypes.Hash) error {
2✔
451

2✔
452
        var b bytes.Buffer
2✔
453
        if err := WriteElements(&b, paymentIndexTypeHash, id[:]); err != nil {
2✔
NEW
454
                return err
×
NEW
455
        }
×
456

457
        indexes := tx.ReadWriteBucket(paymentsIndexBucket)
2✔
458

2✔
459
        return indexes.Put(sequenceNumber, b.Bytes())
2✔
460
}
461

462
// deserializePaymentIndex deserializes a payment index entry. This function
463
// currently only supports deserialization of payment hash indexes, and will
464
// fail for other types.
465
func deserializePaymentIndex(r io.Reader) (lntypes.Hash, error) {
2✔
466
        var (
2✔
467
                indexType   paymentIndexType
2✔
468
                paymentHash []byte
2✔
469
        )
2✔
470

2✔
471
        if err := ReadElements(r, &indexType, &paymentHash); err != nil {
2✔
NEW
472
                return lntypes.Hash{}, err
×
NEW
473
        }
×
474

475
        // While we only have on payment index type, we do not need to use our
476
        // index type to deserialize the index. However, we sanity check that
477
        // this type is as expected, since we had to read it out anyway.
478
        if indexType != paymentIndexTypeHash {
2✔
NEW
479
                return lntypes.Hash{}, fmt.Errorf("unknown payment index "+
×
NEW
480
                        "type: %v", indexType)
×
NEW
481
        }
×
482

483
        hash, err := lntypes.MakeHash(paymentHash)
2✔
484
        if err != nil {
2✔
NEW
485
                return lntypes.Hash{}, err
×
NEW
486
        }
×
487

488
        return hash, nil
2✔
489
}
490

491
// RegisterAttempt atomically records the provided HTLCAttemptInfo to the
492
// DB.
493
func (p *KVPaymentsDB) RegisterAttempt(paymentHash lntypes.Hash,
494
        attempt *HTLCAttemptInfo) (*MPPayment, error) {
2✔
495

2✔
496
        // Serialize the information before opening the db transaction.
2✔
497
        var a bytes.Buffer
2✔
498
        err := serializeHTLCAttemptInfo(&a, attempt)
2✔
499
        if err != nil {
2✔
NEW
500
                return nil, err
×
NEW
501
        }
×
502
        htlcInfoBytes := a.Bytes()
2✔
503

2✔
504
        htlcIDBytes := make([]byte, 8)
2✔
505
        binary.BigEndian.PutUint64(htlcIDBytes, attempt.AttemptID)
2✔
506

2✔
507
        var payment *MPPayment
2✔
508
        err = kvdb.Batch(p.db.Backend, func(tx kvdb.RwTx) error {
4✔
509
                prefetchPayment(tx, paymentHash)
2✔
510
                bucket, err := fetchPaymentBucketUpdate(tx, paymentHash)
2✔
511
                if err != nil {
2✔
NEW
512
                        return err
×
NEW
513
                }
×
514

515
                payment, err = fetchPayment(bucket)
2✔
516
                if err != nil {
2✔
NEW
517
                        return err
×
NEW
518
                }
×
519

520
                // Check if registering a new attempt is allowed.
521
                if err := payment.Registrable(); err != nil {
2✔
NEW
522
                        return err
×
NEW
523
                }
×
524

525
                // If the final hop has encrypted data, then we know this is a
526
                // blinded payment. In blinded payments, MPP records are not set
527
                // for split payments and the recipient is responsible for using
528
                // a consistent PathID across the various encrypted data
529
                // payloads that we received from them for this payment. All we
530
                // need to check is that the total amount field for each HTLC
531
                // in the split payment is correct.
532
                isBlinded := len(attempt.Route.FinalHop().EncryptedData) != 0
2✔
533

2✔
534
                // Make sure any existing shards match the new one with regards
2✔
535
                // to MPP options.
2✔
536
                mpp := attempt.Route.FinalHop().MPP
2✔
537

2✔
538
                // MPP records should not be set for attempts to blinded paths.
2✔
539
                if isBlinded && mpp != nil {
2✔
NEW
540
                        return ErrMPPRecordInBlindedPayment
×
NEW
541
                }
×
542

543
                for _, h := range payment.InFlightHTLCs() {
4✔
544
                        hMpp := h.Route.FinalHop().MPP
2✔
545

2✔
546
                        // If this is a blinded payment, then no existing HTLCs
2✔
547
                        // should have MPP records.
2✔
548
                        if isBlinded && hMpp != nil {
2✔
NEW
549
                                return ErrMPPRecordInBlindedPayment
×
NEW
550
                        }
×
551

552
                        // If this is a blinded payment, then we just need to
553
                        // check that the TotalAmtMsat field for this shard
554
                        // is equal to that of any other shard in the same
555
                        // payment.
556
                        if isBlinded {
4✔
557
                                if attempt.Route.FinalHop().TotalAmtMsat !=
2✔
558
                                        h.Route.FinalHop().TotalAmtMsat {
2✔
NEW
559

×
NEW
560
                                        //nolint:ll
×
NEW
561
                                        return ErrBlindedPaymentTotalAmountMismatch
×
NEW
562
                                }
×
563

564
                                continue
2✔
565
                        }
566

567
                        switch {
2✔
568
                        // We tried to register a non-MPP attempt for a MPP
569
                        // payment.
NEW
570
                        case mpp == nil && hMpp != nil:
×
NEW
571
                                return ErrMPPayment
×
572

573
                        // We tried to register a MPP shard for a non-MPP
574
                        // payment.
NEW
575
                        case mpp != nil && hMpp == nil:
×
NEW
576
                                return ErrNonMPPayment
×
577

578
                        // Non-MPP payment, nothing more to validate.
NEW
579
                        case mpp == nil:
×
NEW
580
                                continue
×
581
                        }
582

583
                        // Check that MPP options match.
584
                        if mpp.PaymentAddr() != hMpp.PaymentAddr() {
2✔
NEW
585
                                return ErrMPPPaymentAddrMismatch
×
NEW
586
                        }
×
587

588
                        if mpp.TotalMsat() != hMpp.TotalMsat() {
2✔
NEW
589
                                return ErrMPPTotalAmountMismatch
×
NEW
590
                        }
×
591
                }
592

593
                // If this is a non-MPP attempt, it must match the total amount
594
                // exactly. Note that a blinded payment is considered an MPP
595
                // attempt.
596
                amt := attempt.Route.ReceiverAmt()
2✔
597
                if !isBlinded && mpp == nil && amt != payment.Info.Value {
2✔
NEW
598
                        return ErrValueMismatch
×
NEW
599
                }
×
600

601
                // Ensure we aren't sending more than the total payment amount.
602
                sentAmt, _ := payment.SentAmt()
2✔
603
                if sentAmt+amt > payment.Info.Value {
2✔
NEW
604
                        return fmt.Errorf("%w: attempted=%v, payment amount="+
×
NEW
605
                                "%v", ErrValueExceedsAmt, sentAmt+amt,
×
NEW
606
                                payment.Info.Value)
×
NEW
607
                }
×
608

609
                htlcsBucket, err := bucket.CreateBucketIfNotExists(
2✔
610
                        paymentHtlcsBucket,
2✔
611
                )
2✔
612
                if err != nil {
2✔
NEW
613
                        return err
×
NEW
614
                }
×
615

616
                err = htlcsBucket.Put(
2✔
617
                        htlcBucketKey(htlcAttemptInfoKey, htlcIDBytes),
2✔
618
                        htlcInfoBytes,
2✔
619
                )
2✔
620
                if err != nil {
2✔
NEW
621
                        return err
×
NEW
622
                }
×
623

624
                // Retrieve attempt info for the notification.
625
                payment, err = fetchPayment(bucket)
2✔
626

2✔
627
                return err
2✔
628
        })
629
        if err != nil {
2✔
NEW
630
                return nil, err
×
NEW
631
        }
×
632

633
        return payment, err
2✔
634
}
635

636
// SettleAttempt marks the given attempt settled with the preimage. If this is
637
// a multi shard payment, this might implicitly mean that the full payment
638
// succeeded.
639
//
640
// After invoking this method, InitPayment should always return an error to
641
// prevent us from making duplicate payments to the same payment hash. The
642
// provided preimage is atomically saved to the DB for record keeping.
643
func (p *KVPaymentsDB) SettleAttempt(hash lntypes.Hash,
644
        attemptID uint64, settleInfo *HTLCSettleInfo) (*MPPayment, error) {
2✔
645

2✔
646
        var b bytes.Buffer
2✔
647
        if err := serializeHTLCSettleInfo(&b, settleInfo); err != nil {
2✔
NEW
648
                return nil, err
×
NEW
649
        }
×
650
        settleBytes := b.Bytes()
2✔
651

2✔
652
        return p.updateHtlcKey(hash, attemptID, htlcSettleInfoKey, settleBytes)
2✔
653
}
654

655
// FailAttempt marks the given payment attempt failed.
656
func (p *KVPaymentsDB) FailAttempt(hash lntypes.Hash,
657
        attemptID uint64, failInfo *HTLCFailInfo) (*MPPayment, error) {
2✔
658

2✔
659
        var b bytes.Buffer
2✔
660
        if err := serializeHTLCFailInfo(&b, failInfo); err != nil {
2✔
NEW
661
                return nil, err
×
NEW
662
        }
×
663
        failBytes := b.Bytes()
2✔
664

2✔
665
        return p.updateHtlcKey(hash, attemptID, htlcFailInfoKey, failBytes)
2✔
666
}
667

668
// updateHtlcKey updates a database key for the specified htlc.
669
func (p *KVPaymentsDB) updateHtlcKey(paymentHash lntypes.Hash,
670
        attemptID uint64, key, value []byte) (*MPPayment, error) {
2✔
671

2✔
672
        aid := make([]byte, 8)
2✔
673
        binary.BigEndian.PutUint64(aid, attemptID)
2✔
674

2✔
675
        var payment *MPPayment
2✔
676
        err := kvdb.Batch(p.db.Backend, func(tx kvdb.RwTx) error {
4✔
677
                payment = nil
2✔
678

2✔
679
                prefetchPayment(tx, paymentHash)
2✔
680
                bucket, err := fetchPaymentBucketUpdate(tx, paymentHash)
2✔
681
                if err != nil {
2✔
NEW
682
                        return err
×
NEW
683
                }
×
684

685
                p, err := fetchPayment(bucket)
2✔
686
                if err != nil {
2✔
NEW
687
                        return err
×
NEW
688
                }
×
689

690
                // We can only update keys of in-flight payments. We allow
691
                // updating keys even if the payment has reached a terminal
692
                // condition, since the HTLC outcomes must still be updated.
693
                if err := p.Status.updatable(); err != nil {
2✔
NEW
694
                        return err
×
NEW
695
                }
×
696

697
                htlcsBucket := bucket.NestedReadWriteBucket(paymentHtlcsBucket)
2✔
698
                if htlcsBucket == nil {
2✔
NEW
699
                        return fmt.Errorf("htlcs bucket not found")
×
NEW
700
                }
×
701

702
                if htlcsBucket.Get(htlcBucketKey(htlcAttemptInfoKey, aid)) == nil {
2✔
NEW
703
                        return fmt.Errorf("HTLC with ID %v not registered",
×
NEW
704
                                attemptID)
×
NEW
705
                }
×
706

707
                // Make sure the shard is not already failed or settled.
708
                if htlcsBucket.Get(htlcBucketKey(htlcFailInfoKey, aid)) != nil {
2✔
NEW
709
                        return ErrAttemptAlreadyFailed
×
NEW
710
                }
×
711

712
                if htlcsBucket.Get(htlcBucketKey(htlcSettleInfoKey, aid)) != nil {
2✔
NEW
713
                        return ErrAttemptAlreadySettled
×
NEW
714
                }
×
715

716
                // Add or update the key for this htlc.
717
                err = htlcsBucket.Put(htlcBucketKey(key, aid), value)
2✔
718
                if err != nil {
2✔
NEW
719
                        return err
×
NEW
720
                }
×
721

722
                // Retrieve attempt info for the notification.
723
                payment, err = fetchPayment(bucket)
2✔
724
                return err
2✔
725
        })
726
        if err != nil {
2✔
NEW
727
                return nil, err
×
NEW
728
        }
×
729

730
        return payment, err
2✔
731
}
732

733
// Fail transitions a payment into the Failed state, and records the reason the
734
// payment failed. After invoking this method, InitPayment should return nil on
735
// its next call for this payment hash, allowing the switch to make a
736
// subsequent payment.
737
func (p *KVPaymentsDB) Fail(paymentHash lntypes.Hash,
738
        reason FailureReason) (*MPPayment, error) {
2✔
739

2✔
740
        var (
2✔
741
                updateErr error
2✔
742
                payment   *MPPayment
2✔
743
        )
2✔
744
        err := kvdb.Batch(p.db.Backend, func(tx kvdb.RwTx) error {
4✔
745
                // Reset the update error, to avoid carrying over an error
2✔
746
                // from a previous execution of the batched db transaction.
2✔
747
                updateErr = nil
2✔
748
                payment = nil
2✔
749

2✔
750
                prefetchPayment(tx, paymentHash)
2✔
751
                bucket, err := fetchPaymentBucketUpdate(tx, paymentHash)
2✔
752
                if err == ErrPaymentNotInitiated {
2✔
NEW
753
                        updateErr = ErrPaymentNotInitiated
×
NEW
754
                        return nil
×
755
                } else if err != nil {
2✔
NEW
756
                        return err
×
NEW
757
                }
×
758

759
                // We mark the payment as failed as long as it is known. This
760
                // lets the last attempt to fail with a terminal write its
761
                // failure to the KVPaymentsDB without synchronizing with
762
                // other attempts.
763
                _, err = fetchPaymentStatus(bucket)
2✔
764
                if errors.Is(err, ErrPaymentNotInitiated) {
2✔
NEW
765
                        updateErr = ErrPaymentNotInitiated
×
NEW
766
                        return nil
×
767
                } else if err != nil {
2✔
NEW
768
                        return err
×
NEW
769
                }
×
770

771
                // Put the failure reason in the bucket for record keeping.
772
                v := []byte{byte(reason)}
2✔
773
                err = bucket.Put(paymentFailInfoKey, v)
2✔
774
                if err != nil {
2✔
NEW
775
                        return err
×
NEW
776
                }
×
777

778
                // Retrieve attempt info for the notification, if available.
779
                payment, err = fetchPayment(bucket)
2✔
780
                if err != nil {
2✔
NEW
781
                        return err
×
NEW
782
                }
×
783

784
                return nil
2✔
785
        })
786
        if err != nil {
2✔
NEW
787
                return nil, err
×
NEW
788
        }
×
789

790
        return payment, updateErr
2✔
791
}
792

793
// FetchPayment returns information about a payment from the database.
794
func (p *KVPaymentsDB) FetchPayment(paymentHash lntypes.Hash) (
795
        *MPPayment, error) {
2✔
796

2✔
797
        var payment *MPPayment
2✔
798
        err := kvdb.View(p.db, func(tx kvdb.RTx) error {
4✔
799
                prefetchPayment(tx, paymentHash)
2✔
800
                bucket, err := fetchPaymentBucket(tx, paymentHash)
2✔
801
                if err != nil {
2✔
NEW
802
                        return err
×
NEW
803
                }
×
804

805
                payment, err = fetchPayment(bucket)
2✔
806

2✔
807
                return err
2✔
808
        }, func() {
2✔
809
                payment = nil
2✔
810
        })
2✔
811
        if err != nil {
2✔
NEW
812
                return nil, err
×
NEW
813
        }
×
814

815
        return payment, nil
2✔
816
}
817

818
// prefetchPayment attempts to prefetch as much of the payment as possible to
819
// reduce DB roundtrips.
820
func prefetchPayment(tx kvdb.RTx, paymentHash lntypes.Hash) {
2✔
821
        rb := kvdb.RootBucket(tx)
2✔
822
        kvdb.Prefetch(
2✔
823
                rb,
2✔
824
                []string{
2✔
825
                        // Prefetch all keys in the payment's bucket.
2✔
826
                        string(paymentsRootBucket),
2✔
827
                        string(paymentHash[:]),
2✔
828
                },
2✔
829
                []string{
2✔
830
                        // Prefetch all keys in the payment's htlc bucket.
2✔
831
                        string(paymentsRootBucket),
2✔
832
                        string(paymentHash[:]),
2✔
833
                        string(paymentHtlcsBucket),
2✔
834
                },
2✔
835
        )
2✔
836
}
2✔
837

838
// createPaymentBucket creates or fetches the sub-bucket assigned to this
839
// payment hash.
840
func createPaymentBucket(tx kvdb.RwTx, paymentHash lntypes.Hash) (
841
        kvdb.RwBucket, error) {
2✔
842

2✔
843
        payments, err := tx.CreateTopLevelBucket(paymentsRootBucket)
2✔
844
        if err != nil {
2✔
NEW
845
                return nil, err
×
NEW
846
        }
×
847

848
        return payments.CreateBucketIfNotExists(paymentHash[:])
2✔
849
}
850

851
// fetchPaymentBucket fetches the sub-bucket assigned to this payment hash. If
852
// the bucket does not exist, it returns ErrPaymentNotInitiated.
853
func fetchPaymentBucket(tx kvdb.RTx, paymentHash lntypes.Hash) (
854
        kvdb.RBucket, error) {
2✔
855

2✔
856
        payments := tx.ReadBucket(paymentsRootBucket)
2✔
857
        if payments == nil {
2✔
NEW
858
                return nil, ErrPaymentNotInitiated
×
NEW
859
        }
×
860

861
        bucket := payments.NestedReadBucket(paymentHash[:])
2✔
862
        if bucket == nil {
2✔
NEW
863
                return nil, ErrPaymentNotInitiated
×
NEW
864
        }
×
865

866
        return bucket, nil
2✔
867
}
868

869
// fetchPaymentBucketUpdate is identical to fetchPaymentBucket, but it returns a
870
// bucket that can be written to.
871
func fetchPaymentBucketUpdate(tx kvdb.RwTx, paymentHash lntypes.Hash) (
872
        kvdb.RwBucket, error) {
2✔
873

2✔
874
        payments := tx.ReadWriteBucket(paymentsRootBucket)
2✔
875
        if payments == nil {
2✔
NEW
876
                return nil, ErrPaymentNotInitiated
×
NEW
877
        }
×
878

879
        bucket := payments.NestedReadWriteBucket(paymentHash[:])
2✔
880
        if bucket == nil {
2✔
NEW
881
                return nil, ErrPaymentNotInitiated
×
NEW
882
        }
×
883

884
        return bucket, nil
2✔
885
}
886

887
// nextPaymentSequence returns the next sequence number to store for a new
888
// payment.
889
func (p *KVPaymentsDB) nextPaymentSequence() ([]byte, error) {
2✔
890
        p.paymentSeqMx.Lock()
2✔
891
        defer p.paymentSeqMx.Unlock()
2✔
892

2✔
893
        // Set a new upper bound in the DB every 1000 payments to avoid
2✔
894
        // conflicts on the sequence when using etcd.
2✔
895
        if p.currPaymentSeq == p.storedPaymentSeq {
4✔
896
                var currPaymentSeq, newUpperBound uint64
2✔
897
                if err := kvdb.Update(p.db.Backend, func(tx kvdb.RwTx) error {
4✔
898
                        paymentsBucket, err := tx.CreateTopLevelBucket(
2✔
899
                                paymentsRootBucket,
2✔
900
                        )
2✔
901
                        if err != nil {
2✔
NEW
902
                                return err
×
NEW
903
                        }
×
904

905
                        currPaymentSeq = paymentsBucket.Sequence()
2✔
906
                        newUpperBound = currPaymentSeq + paymentSeqBlockSize
2✔
907
                        return paymentsBucket.SetSequence(newUpperBound)
2✔
908
                }, func() {}); err != nil {
2✔
NEW
909
                        return nil, err
×
NEW
910
                }
×
911

912
                // We lazy initialize the cached currPaymentSeq here using the
913
                // first nextPaymentSequence() call. This if statement will auto
914
                // initialize our stored currPaymentSeq, since by default both
915
                // this variable and storedPaymentSeq are zero which in turn
916
                // will have us fetch the current values from the DB.
917
                if p.currPaymentSeq == 0 {
4✔
918
                        p.currPaymentSeq = currPaymentSeq
2✔
919
                }
2✔
920

921
                p.storedPaymentSeq = newUpperBound
2✔
922
        }
923

924
        p.currPaymentSeq++
2✔
925
        b := make([]byte, 8)
2✔
926
        binary.BigEndian.PutUint64(b, p.currPaymentSeq)
2✔
927

2✔
928
        return b, nil
2✔
929
}
930

931
// fetchPaymentStatus fetches the payment status of the payment. If the payment
932
// isn't found, it will return error `ErrPaymentNotInitiated`.
933
func fetchPaymentStatus(bucket kvdb.RBucket) (PaymentStatus, error) {
2✔
934
        // Creation info should be set for all payments, regardless of state.
2✔
935
        // If not, it is unknown.
2✔
936
        if bucket.Get(paymentCreationInfoKey) == nil {
4✔
937
                return 0, ErrPaymentNotInitiated
2✔
938
        }
2✔
939

940
        payment, err := fetchPayment(bucket)
2✔
941
        if err != nil {
2✔
NEW
942
                return 0, err
×
NEW
943
        }
×
944

945
        return payment.Status, nil
2✔
946
}
947

948
// FetchInFlightPayments returns all payments with status InFlight.
949
func (p *KVPaymentsDB) FetchInFlightPayments() ([]*MPPayment, error) {
2✔
950
        var (
2✔
951
                inFlights      []*MPPayment
2✔
952
                start          = time.Now()
2✔
953
                lastLogTime    = time.Now()
2✔
954
                processedCount int
2✔
955
        )
2✔
956

2✔
957
        err := kvdb.View(p.db, func(tx kvdb.RTx) error {
4✔
958
                payments := tx.ReadBucket(paymentsRootBucket)
2✔
959
                if payments == nil {
4✔
960
                        return nil
2✔
961
                }
2✔
962

963
                return payments.ForEach(func(k, _ []byte) error {
4✔
964
                        bucket := payments.NestedReadBucket(k)
2✔
965
                        if bucket == nil {
2✔
NEW
966
                                return fmt.Errorf("non bucket element")
×
NEW
967
                        }
×
968

969
                        p, err := fetchPayment(bucket)
2✔
970
                        if err != nil {
2✔
NEW
971
                                return err
×
NEW
972
                        }
×
973

974
                        processedCount++
2✔
975
                        if time.Since(lastLogTime) >=
2✔
976
                                paymentProgressLogInterval {
2✔
NEW
977

×
NEW
978
                                log.Debugf("Scanning inflight payments "+
×
NEW
979
                                        "(in progress), processed %d, last "+
×
NEW
980
                                        "processed payment: %v", processedCount,
×
NEW
981
                                        p.Info)
×
NEW
982

×
NEW
983
                                lastLogTime = time.Now()
×
NEW
984
                        }
×
985

986
                        // Skip the payment if it's terminated.
987
                        if p.Terminated() {
4✔
988
                                return nil
2✔
989
                        }
2✔
990

991
                        inFlights = append(inFlights, p)
2✔
992
                        return nil
2✔
993
                })
994
        }, func() {
2✔
995
                inFlights = nil
2✔
996
        })
2✔
997
        if err != nil {
2✔
NEW
998
                return nil, err
×
NEW
999
        }
×
1000

1001
        elapsed := time.Since(start)
2✔
1002
        log.Debugf("Completed scanning for inflight payments: "+
2✔
1003
                "total_processed=%d, found_inflight=%d, elapsed=%v",
2✔
1004
                processedCount, len(inFlights),
2✔
1005
                elapsed.Round(time.Millisecond))
2✔
1006

2✔
1007
        return inFlights, nil
2✔
1008
}
1009

1010
// PaymentCreationInfo is the information necessary to have ready when
1011
// initiating a payment, moving it into state InFlight.
1012
type PaymentCreationInfo struct {
1013
        // PaymentIdentifier is the hash this payment is paying to in case of
1014
        // non-AMP payments, and the SetID for AMP payments.
1015
        PaymentIdentifier lntypes.Hash
1016

1017
        // Value is the amount we are paying.
1018
        Value lnwire.MilliSatoshi
1019

1020
        // CreationTime is the time when this payment was initiated.
1021
        CreationTime time.Time
1022

1023
        // PaymentRequest is the full payment request, if any.
1024
        PaymentRequest []byte
1025

1026
        // FirstHopCustomRecords are the TLV records that are to be sent to the
1027
        // first hop of this payment. These records will be transmitted via the
1028
        // wire message only and therefore do not affect the onion payload size.
1029
        FirstHopCustomRecords lnwire.CustomRecords
1030
}
1031

1032
// String returns a human-readable description of the payment creation info.
UNCOV
1033
func (p *PaymentCreationInfo) String() string {
×
UNCOV
1034
        return fmt.Sprintf("payment_id=%v, amount=%v, created_at=%v",
×
UNCOV
1035
                p.PaymentIdentifier, p.Value, p.CreationTime)
×
UNCOV
1036
}
×
1037

1038
// htlcBucketKey creates a composite key from prefix and id where the result is
1039
// simply the two concatenated.
1040
func htlcBucketKey(prefix, id []byte) []byte {
2✔
1041
        key := make([]byte, len(prefix)+len(id))
2✔
1042
        copy(key, prefix)
2✔
1043
        copy(key[len(prefix):], id)
2✔
1044
        return key
2✔
1045
}
2✔
1046

1047
// FetchPayments returns all sent payments found in the DB.
1048
//
1049
// nolint: dupl
UNCOV
1050
func (d *DB) FetchPayments() ([]*MPPayment, error) {
×
UNCOV
1051
        var payments []*MPPayment
×
UNCOV
1052

×
UNCOV
1053
        err := kvdb.View(d, func(tx kvdb.RTx) error {
×
UNCOV
1054
                paymentsBucket := tx.ReadBucket(paymentsRootBucket)
×
UNCOV
1055
                if paymentsBucket == nil {
×
1056
                        return nil
×
1057
                }
×
1058

UNCOV
1059
                return paymentsBucket.ForEach(func(k, v []byte) error {
×
UNCOV
1060
                        bucket := paymentsBucket.NestedReadBucket(k)
×
UNCOV
1061
                        if bucket == nil {
×
1062
                                // We only expect sub-buckets to be found in
×
1063
                                // this top-level bucket.
×
1064
                                return fmt.Errorf("non bucket element in " +
×
1065
                                        "payments bucket")
×
1066
                        }
×
1067

UNCOV
1068
                        p, err := fetchPayment(bucket)
×
UNCOV
1069
                        if err != nil {
×
1070
                                return err
×
1071
                        }
×
1072

UNCOV
1073
                        payments = append(payments, p)
×
UNCOV
1074

×
UNCOV
1075
                        // For older versions of lnd, duplicate payments to a
×
UNCOV
1076
                        // payment has was possible. These will be found in a
×
UNCOV
1077
                        // sub-bucket indexed by their sequence number if
×
UNCOV
1078
                        // available.
×
UNCOV
1079
                        duplicatePayments, err := fetchDuplicatePayments(bucket)
×
UNCOV
1080
                        if err != nil {
×
1081
                                return err
×
1082
                        }
×
1083

UNCOV
1084
                        payments = append(payments, duplicatePayments...)
×
UNCOV
1085
                        return nil
×
1086
                })
UNCOV
1087
        }, func() {
×
UNCOV
1088
                payments = nil
×
UNCOV
1089
        })
×
UNCOV
1090
        if err != nil {
×
1091
                return nil, err
×
1092
        }
×
1093

1094
        // Before returning, sort the payments by their sequence number.
UNCOV
1095
        sort.Slice(payments, func(i, j int) bool {
×
UNCOV
1096
                return payments[i].SequenceNum < payments[j].SequenceNum
×
UNCOV
1097
        })
×
1098

UNCOV
1099
        return payments, nil
×
1100
}
1101

1102
func fetchCreationInfo(bucket kvdb.RBucket) (*PaymentCreationInfo, error) {
2✔
1103
        b := bucket.Get(paymentCreationInfoKey)
2✔
1104
        if b == nil {
2✔
1105
                return nil, fmt.Errorf("creation info not found")
×
1106
        }
×
1107

1108
        r := bytes.NewReader(b)
2✔
1109
        return deserializePaymentCreationInfo(r)
2✔
1110
}
1111

1112
func fetchPayment(bucket kvdb.RBucket) (*MPPayment, error) {
2✔
1113
        seqBytes := bucket.Get(paymentSequenceKey)
2✔
1114
        if seqBytes == nil {
2✔
1115
                return nil, fmt.Errorf("sequence number not found")
×
1116
        }
×
1117

1118
        sequenceNum := binary.BigEndian.Uint64(seqBytes)
2✔
1119

2✔
1120
        // Get the PaymentCreationInfo.
2✔
1121
        creationInfo, err := fetchCreationInfo(bucket)
2✔
1122
        if err != nil {
2✔
1123
                return nil, err
×
1124
        }
×
1125

1126
        var htlcs []HTLCAttempt
2✔
1127
        htlcsBucket := bucket.NestedReadBucket(paymentHtlcsBucket)
2✔
1128
        if htlcsBucket != nil {
4✔
1129
                // Get the payment attempts. This can be empty.
2✔
1130
                htlcs, err = fetchHtlcAttempts(htlcsBucket)
2✔
1131
                if err != nil {
2✔
1132
                        return nil, err
×
1133
                }
×
1134
        }
1135

1136
        // Get failure reason if available.
1137
        var failureReason *FailureReason
2✔
1138
        b := bucket.Get(paymentFailInfoKey)
2✔
1139
        if b != nil {
4✔
1140
                reason := FailureReason(b[0])
2✔
1141
                failureReason = &reason
2✔
1142
        }
2✔
1143

1144
        // Create a new payment.
1145
        payment := &MPPayment{
2✔
1146
                SequenceNum:   sequenceNum,
2✔
1147
                Info:          creationInfo,
2✔
1148
                HTLCs:         htlcs,
2✔
1149
                FailureReason: failureReason,
2✔
1150
        }
2✔
1151

2✔
1152
        // Set its state and status.
2✔
1153
        if err := payment.setState(); err != nil {
2✔
1154
                return nil, err
×
1155
        }
×
1156

1157
        return payment, nil
2✔
1158
}
1159

1160
// fetchHtlcAttempts retrieves all htlc attempts made for the payment found in
1161
// the given bucket.
1162
func fetchHtlcAttempts(bucket kvdb.RBucket) ([]HTLCAttempt, error) {
2✔
1163
        htlcsMap := make(map[uint64]*HTLCAttempt)
2✔
1164

2✔
1165
        attemptInfoCount := 0
2✔
1166
        err := bucket.ForEach(func(k, v []byte) error {
4✔
1167
                aid := byteOrder.Uint64(k[len(k)-8:])
2✔
1168

2✔
1169
                if _, ok := htlcsMap[aid]; !ok {
4✔
1170
                        htlcsMap[aid] = &HTLCAttempt{}
2✔
1171
                }
2✔
1172

1173
                var err error
2✔
1174
                switch {
2✔
1175
                case bytes.HasPrefix(k, htlcAttemptInfoKey):
2✔
1176
                        attemptInfo, err := readHtlcAttemptInfo(v)
2✔
1177
                        if err != nil {
2✔
1178
                                return err
×
1179
                        }
×
1180

1181
                        attemptInfo.AttemptID = aid
2✔
1182
                        htlcsMap[aid].HTLCAttemptInfo = *attemptInfo
2✔
1183
                        attemptInfoCount++
2✔
1184

1185
                case bytes.HasPrefix(k, htlcSettleInfoKey):
2✔
1186
                        htlcsMap[aid].Settle, err = readHtlcSettleInfo(v)
2✔
1187
                        if err != nil {
2✔
1188
                                return err
×
1189
                        }
×
1190

1191
                case bytes.HasPrefix(k, htlcFailInfoKey):
2✔
1192
                        htlcsMap[aid].Failure, err = readHtlcFailInfo(v)
2✔
1193
                        if err != nil {
2✔
1194
                                return err
×
1195
                        }
×
1196

1197
                default:
×
1198
                        return fmt.Errorf("unknown htlc attempt key")
×
1199
                }
1200

1201
                return nil
2✔
1202
        })
1203
        if err != nil {
2✔
1204
                return nil, err
×
1205
        }
×
1206

1207
        // Sanity check that all htlcs have an attempt info.
1208
        if attemptInfoCount != len(htlcsMap) {
2✔
1209
                return nil, errNoAttemptInfo
×
1210
        }
×
1211

1212
        keys := make([]uint64, len(htlcsMap))
2✔
1213
        i := 0
2✔
1214
        for k := range htlcsMap {
4✔
1215
                keys[i] = k
2✔
1216
                i++
2✔
1217
        }
2✔
1218

1219
        // Sort HTLC attempts by their attempt ID. This is needed because in the
1220
        // DB we store the attempts with keys prefixed by their status which
1221
        // changes order (groups them together by status).
1222
        sort.Slice(keys, func(i, j int) bool {
4✔
1223
                return keys[i] < keys[j]
2✔
1224
        })
2✔
1225

1226
        htlcs := make([]HTLCAttempt, len(htlcsMap))
2✔
1227
        for i, key := range keys {
4✔
1228
                htlcs[i] = *htlcsMap[key]
2✔
1229
        }
2✔
1230

1231
        return htlcs, nil
2✔
1232
}
1233

1234
// readHtlcAttemptInfo reads the payment attempt info for this htlc.
1235
func readHtlcAttemptInfo(b []byte) (*HTLCAttemptInfo, error) {
2✔
1236
        r := bytes.NewReader(b)
2✔
1237
        return deserializeHTLCAttemptInfo(r)
2✔
1238
}
2✔
1239

1240
// readHtlcSettleInfo reads the settle info for the htlc. If the htlc isn't
1241
// settled, nil is returned.
1242
func readHtlcSettleInfo(b []byte) (*HTLCSettleInfo, error) {
2✔
1243
        r := bytes.NewReader(b)
2✔
1244
        return deserializeHTLCSettleInfo(r)
2✔
1245
}
2✔
1246

1247
// readHtlcFailInfo reads the failure info for the htlc. If the htlc hasn't
1248
// failed, nil is returned.
1249
func readHtlcFailInfo(b []byte) (*HTLCFailInfo, error) {
2✔
1250
        r := bytes.NewReader(b)
2✔
1251
        return deserializeHTLCFailInfo(r)
2✔
1252
}
2✔
1253

1254
// fetchFailedHtlcKeys retrieves the bucket keys of all failed HTLCs of a
1255
// payment bucket.
UNCOV
1256
func fetchFailedHtlcKeys(bucket kvdb.RBucket) ([][]byte, error) {
×
UNCOV
1257
        htlcsBucket := bucket.NestedReadBucket(paymentHtlcsBucket)
×
UNCOV
1258

×
UNCOV
1259
        var htlcs []HTLCAttempt
×
UNCOV
1260
        var err error
×
UNCOV
1261
        if htlcsBucket != nil {
×
UNCOV
1262
                htlcs, err = fetchHtlcAttempts(htlcsBucket)
×
UNCOV
1263
                if err != nil {
×
1264
                        return nil, err
×
1265
                }
×
1266
        }
1267

1268
        // Now iterate though them and save the bucket keys for the failed
1269
        // HTLCs.
UNCOV
1270
        var htlcKeys [][]byte
×
UNCOV
1271
        for _, h := range htlcs {
×
UNCOV
1272
                if h.Failure == nil {
×
UNCOV
1273
                        continue
×
1274
                }
1275

UNCOV
1276
                htlcKeyBytes := make([]byte, 8)
×
UNCOV
1277
                binary.BigEndian.PutUint64(htlcKeyBytes, h.AttemptID)
×
UNCOV
1278

×
UNCOV
1279
                htlcKeys = append(htlcKeys, htlcKeyBytes)
×
1280
        }
1281

UNCOV
1282
        return htlcKeys, nil
×
1283
}
1284

1285
// PaymentsQuery represents a query to the payments database starting or ending
1286
// at a certain offset index. The number of retrieved records can be limited.
1287
type PaymentsQuery struct {
1288
        // IndexOffset determines the starting point of the payments query and
1289
        // is always exclusive. In normal order, the query starts at the next
1290
        // higher (available) index compared to IndexOffset. In reversed order,
1291
        // the query ends at the next lower (available) index compared to the
1292
        // IndexOffset. In the case of a zero index_offset, the query will start
1293
        // with the oldest payment when paginating forwards, or will end with
1294
        // the most recent payment when paginating backwards.
1295
        IndexOffset uint64
1296

1297
        // MaxPayments is the maximal number of payments returned in the
1298
        // payments query.
1299
        MaxPayments uint64
1300

1301
        // Reversed gives a meaning to the IndexOffset. If reversed is set to
1302
        // true, the query will fetch payments with indices lower than the
1303
        // IndexOffset, otherwise, it will return payments with indices greater
1304
        // than the IndexOffset.
1305
        Reversed bool
1306

1307
        // If IncludeIncomplete is true, then return payments that have not yet
1308
        // fully completed. This means that pending payments, as well as failed
1309
        // payments will show up if this field is set to true.
1310
        IncludeIncomplete bool
1311

1312
        // CountTotal indicates that all payments currently present in the
1313
        // payment index (complete and incomplete) should be counted.
1314
        CountTotal bool
1315

1316
        // CreationDateStart, expressed in Unix seconds, if set, filters out
1317
        // all payments with a creation date greater than or equal to it.
1318
        CreationDateStart int64
1319

1320
        // CreationDateEnd, expressed in Unix seconds, if set, filters out all
1321
        // payments with a creation date less than or equal to it.
1322
        CreationDateEnd int64
1323
}
1324

1325
// PaymentsResponse contains the result of a query to the payments database.
1326
// It includes the set of payments that match the query and integers which
1327
// represent the index of the first and last item returned in the series of
1328
// payments. These integers allow callers to resume their query in the event
1329
// that the query's response exceeds the max number of returnable events.
1330
type PaymentsResponse struct {
1331
        // Payments is the set of payments returned from the database for the
1332
        // PaymentsQuery.
1333
        Payments []*MPPayment
1334

1335
        // FirstIndexOffset is the index of the first element in the set of
1336
        // returned MPPayments. Callers can use this to resume their query
1337
        // in the event that the slice has too many events to fit into a single
1338
        // response. The offset can be used to continue reverse pagination.
1339
        FirstIndexOffset uint64
1340

1341
        // LastIndexOffset is the index of the last element in the set of
1342
        // returned MPPayments. Callers can use this to resume their query
1343
        // in the event that the slice has too many events to fit into a single
1344
        // response. The offset can be used to continue forward pagination.
1345
        LastIndexOffset uint64
1346

1347
        // TotalCount represents the total number of payments that are currently
1348
        // stored in the payment database. This will only be set if the
1349
        // CountTotal field in the query was set to true.
1350
        TotalCount uint64
1351
}
1352

1353
// QueryPayments is a query to the payments database which is restricted
1354
// to a subset of payments by the payments query, containing an offset
1355
// index and a maximum number of returned payments.
1356
func (d *DB) QueryPayments(query PaymentsQuery) (PaymentsResponse, error) {
2✔
1357
        var resp PaymentsResponse
2✔
1358

2✔
1359
        if err := kvdb.View(d, func(tx kvdb.RTx) error {
4✔
1360
                // Get the root payments bucket.
2✔
1361
                paymentsBucket := tx.ReadBucket(paymentsRootBucket)
2✔
1362
                if paymentsBucket == nil {
4✔
1363
                        return nil
2✔
1364
                }
2✔
1365

1366
                // Get the index bucket which maps sequence number -> payment
1367
                // hash and duplicate bool. If we have a payments bucket, we
1368
                // should have an indexes bucket as well.
1369
                indexes := tx.ReadBucket(paymentsIndexBucket)
2✔
1370
                if indexes == nil {
2✔
1371
                        return fmt.Errorf("index bucket does not exist")
×
1372
                }
×
1373

1374
                // accumulatePayments gets payments with the sequence number
1375
                // and hash provided and adds them to our list of payments if
1376
                // they meet the criteria of our query. It returns the number
1377
                // of payments that were added.
1378
                accumulatePayments := func(sequenceKey, hash []byte) (bool,
2✔
1379
                        error) {
4✔
1380

2✔
1381
                        r := bytes.NewReader(hash)
2✔
1382
                        paymentHash, err := deserializePaymentIndex(r)
2✔
1383
                        if err != nil {
2✔
1384
                                return false, err
×
1385
                        }
×
1386

1387
                        payment, err := fetchPaymentWithSequenceNumber(
2✔
1388
                                tx, paymentHash, sequenceKey,
2✔
1389
                        )
2✔
1390
                        if err != nil {
2✔
1391
                                return false, err
×
1392
                        }
×
1393

1394
                        // To keep compatibility with the old API, we only
1395
                        // return non-succeeded payments if requested.
1396
                        if payment.Status != StatusSucceeded &&
2✔
1397
                                !query.IncludeIncomplete {
2✔
UNCOV
1398

×
UNCOV
1399
                                return false, err
×
UNCOV
1400
                        }
×
1401

1402
                        // Get the creation time in Unix seconds, this always
1403
                        // rounds down the nanoseconds to full seconds.
1404
                        createTime := payment.Info.CreationTime.Unix()
2✔
1405

2✔
1406
                        // Skip any payments that were created before the
2✔
1407
                        // specified time.
2✔
1408
                        if createTime < query.CreationDateStart {
4✔
1409
                                return false, nil
2✔
1410
                        }
2✔
1411

1412
                        // Skip any payments that were created after the
1413
                        // specified time.
1414
                        if query.CreationDateEnd != 0 &&
2✔
1415
                                createTime > query.CreationDateEnd {
4✔
1416

2✔
1417
                                return false, nil
2✔
1418
                        }
2✔
1419

1420
                        // At this point, we've exhausted the offset, so we'll
1421
                        // begin collecting invoices found within the range.
1422
                        resp.Payments = append(resp.Payments, payment)
2✔
1423
                        return true, nil
2✔
1424
                }
1425

1426
                // Create a paginator which reads from our sequence index bucket
1427
                // with the parameters provided by the payments query.
1428
                paginator := newPaginator(
2✔
1429
                        indexes.ReadCursor(), query.Reversed, query.IndexOffset,
2✔
1430
                        query.MaxPayments,
2✔
1431
                )
2✔
1432

2✔
1433
                // Run a paginated query, adding payments to our response.
2✔
1434
                if err := paginator.query(accumulatePayments); err != nil {
2✔
1435
                        return err
×
1436
                }
×
1437

1438
                // Counting the total number of payments is expensive, since we
1439
                // literally have to traverse the cursor linearly, which can
1440
                // take quite a while. So it's an optional query parameter.
1441
                if query.CountTotal {
2✔
1442
                        var (
×
1443
                                totalPayments uint64
×
1444
                                err           error
×
1445
                        )
×
1446
                        countFn := func(_, _ []byte) error {
×
1447
                                totalPayments++
×
1448

×
1449
                                return nil
×
1450
                        }
×
1451

1452
                        // In non-boltdb database backends, there's a faster
1453
                        // ForAll query that allows for batch fetching items.
1454
                        if fastBucket, ok := indexes.(kvdb.ExtendedRBucket); ok {
×
1455
                                err = fastBucket.ForAll(countFn)
×
1456
                        } else {
×
1457
                                err = indexes.ForEach(countFn)
×
1458
                        }
×
1459
                        if err != nil {
×
1460
                                return fmt.Errorf("error counting payments: %w",
×
1461
                                        err)
×
1462
                        }
×
1463

1464
                        resp.TotalCount = totalPayments
×
1465
                }
1466

1467
                return nil
2✔
1468
        }, func() {
2✔
1469
                resp = PaymentsResponse{}
2✔
1470
        }); err != nil {
2✔
1471
                return resp, err
×
1472
        }
×
1473

1474
        // Need to swap the payments slice order if reversed order.
1475
        if query.Reversed {
2✔
UNCOV
1476
                for l, r := 0, len(resp.Payments)-1; l < r; l, r = l+1, r-1 {
×
UNCOV
1477
                        resp.Payments[l], resp.Payments[r] =
×
UNCOV
1478
                                resp.Payments[r], resp.Payments[l]
×
UNCOV
1479
                }
×
1480
        }
1481

1482
        // Set the first and last index of the returned payments so that the
1483
        // caller can resume from this point later on.
1484
        if len(resp.Payments) > 0 {
4✔
1485
                resp.FirstIndexOffset = resp.Payments[0].SequenceNum
2✔
1486
                resp.LastIndexOffset =
2✔
1487
                        resp.Payments[len(resp.Payments)-1].SequenceNum
2✔
1488
        }
2✔
1489

1490
        return resp, nil
2✔
1491
}
1492

1493
// fetchPaymentWithSequenceNumber get the payment which matches the payment hash
1494
// *and* sequence number provided from the database. This is required because
1495
// we previously had more than one payment per hash, so we have multiple indexes
1496
// pointing to a single payment; we want to retrieve the correct one.
1497
func fetchPaymentWithSequenceNumber(tx kvdb.RTx, paymentHash lntypes.Hash,
1498
        sequenceNumber []byte) (*MPPayment, error) {
2✔
1499

2✔
1500
        // We can now lookup the payment keyed by its hash in
2✔
1501
        // the payments root bucket.
2✔
1502
        bucket, err := fetchPaymentBucket(tx, paymentHash)
2✔
1503
        if err != nil {
2✔
1504
                return nil, err
×
1505
        }
×
1506

1507
        // A single payment hash can have multiple payments associated with it.
1508
        // We lookup our sequence number first, to determine whether this is
1509
        // the payment we are actually looking for.
1510
        seqBytes := bucket.Get(paymentSequenceKey)
2✔
1511
        if seqBytes == nil {
2✔
1512
                return nil, ErrNoSequenceNumber
×
1513
        }
×
1514

1515
        // If this top level payment has the sequence number we are looking for,
1516
        // return it.
1517
        if bytes.Equal(seqBytes, sequenceNumber) {
4✔
1518
                return fetchPayment(bucket)
2✔
1519
        }
2✔
1520

1521
        // If we were not looking for the top level payment, we are looking for
1522
        // one of our duplicate payments. We need to iterate through the seq
1523
        // numbers in this bucket to find the correct payments. If we do not
1524
        // find a duplicate payments bucket here, something is wrong.
UNCOV
1525
        dup := bucket.NestedReadBucket(duplicatePaymentsBucket)
×
UNCOV
1526
        if dup == nil {
×
UNCOV
1527
                return nil, ErrNoDuplicateBucket
×
UNCOV
1528
        }
×
1529

UNCOV
1530
        var duplicatePayment *MPPayment
×
UNCOV
1531
        err = dup.ForEach(func(k, v []byte) error {
×
UNCOV
1532
                subBucket := dup.NestedReadBucket(k)
×
UNCOV
1533
                if subBucket == nil {
×
1534
                        // We one bucket for each duplicate to be found.
×
1535
                        return ErrNoDuplicateNestedBucket
×
1536
                }
×
1537

UNCOV
1538
                seqBytes := subBucket.Get(duplicatePaymentSequenceKey)
×
UNCOV
1539
                if seqBytes == nil {
×
1540
                        return err
×
1541
                }
×
1542

1543
                // If this duplicate payment is not the sequence number we are
1544
                // looking for, we can continue.
UNCOV
1545
                if !bytes.Equal(seqBytes, sequenceNumber) {
×
UNCOV
1546
                        return nil
×
UNCOV
1547
                }
×
1548

UNCOV
1549
                duplicatePayment, err = fetchDuplicatePayment(subBucket)
×
UNCOV
1550
                if err != nil {
×
1551
                        return err
×
1552
                }
×
1553

UNCOV
1554
                return nil
×
1555
        })
UNCOV
1556
        if err != nil {
×
1557
                return nil, err
×
1558
        }
×
1559

1560
        // If none of the duplicate payments matched our sequence number, we
1561
        // failed to find the payment with this sequence number; something is
1562
        // wrong.
UNCOV
1563
        if duplicatePayment == nil {
×
UNCOV
1564
                return nil, ErrDuplicateNotFound
×
UNCOV
1565
        }
×
1566

UNCOV
1567
        return duplicatePayment, nil
×
1568
}
1569

1570
// DeletePayment deletes a payment from the DB given its payment hash. If
1571
// failedHtlcsOnly is set, only failed HTLC attempts of the payment will be
1572
// deleted.
1573
func (d *DB) DeletePayment(paymentHash lntypes.Hash,
UNCOV
1574
        failedHtlcsOnly bool) error {
×
UNCOV
1575

×
UNCOV
1576
        return kvdb.Update(d, func(tx kvdb.RwTx) error {
×
UNCOV
1577
                payments := tx.ReadWriteBucket(paymentsRootBucket)
×
UNCOV
1578
                if payments == nil {
×
1579
                        return nil
×
1580
                }
×
1581

UNCOV
1582
                bucket := payments.NestedReadWriteBucket(paymentHash[:])
×
UNCOV
1583
                if bucket == nil {
×
UNCOV
1584
                        return fmt.Errorf("non bucket element in payments " +
×
UNCOV
1585
                                "bucket")
×
UNCOV
1586
                }
×
1587

1588
                // If the status is InFlight, we cannot safely delete
1589
                // the payment information, so we return early.
UNCOV
1590
                paymentStatus, err := fetchPaymentStatus(bucket)
×
UNCOV
1591
                if err != nil {
×
1592
                        return err
×
1593
                }
×
1594

1595
                // If the payment has inflight HTLCs, we cannot safely delete
1596
                // the payment information, so we return an error.
UNCOV
1597
                if err := paymentStatus.removable(); err != nil {
×
UNCOV
1598
                        return fmt.Errorf("payment '%v' has inflight HTLCs"+
×
UNCOV
1599
                                "and therefore cannot be deleted: %w",
×
UNCOV
1600
                                paymentHash.String(), err)
×
UNCOV
1601
                }
×
1602

1603
                // Delete the failed HTLC attempts we found.
UNCOV
1604
                if failedHtlcsOnly {
×
UNCOV
1605
                        toDelete, err := fetchFailedHtlcKeys(bucket)
×
UNCOV
1606
                        if err != nil {
×
1607
                                return err
×
1608
                        }
×
1609

UNCOV
1610
                        htlcsBucket := bucket.NestedReadWriteBucket(
×
UNCOV
1611
                                paymentHtlcsBucket,
×
UNCOV
1612
                        )
×
UNCOV
1613

×
UNCOV
1614
                        for _, htlcID := range toDelete {
×
UNCOV
1615
                                err = htlcsBucket.Delete(
×
UNCOV
1616
                                        htlcBucketKey(htlcAttemptInfoKey, htlcID),
×
UNCOV
1617
                                )
×
UNCOV
1618
                                if err != nil {
×
1619
                                        return err
×
1620
                                }
×
1621

UNCOV
1622
                                err = htlcsBucket.Delete(
×
UNCOV
1623
                                        htlcBucketKey(htlcFailInfoKey, htlcID),
×
UNCOV
1624
                                )
×
UNCOV
1625
                                if err != nil {
×
1626
                                        return err
×
1627
                                }
×
1628

UNCOV
1629
                                err = htlcsBucket.Delete(
×
UNCOV
1630
                                        htlcBucketKey(htlcSettleInfoKey, htlcID),
×
UNCOV
1631
                                )
×
UNCOV
1632
                                if err != nil {
×
1633
                                        return err
×
1634
                                }
×
1635
                        }
1636

UNCOV
1637
                        return nil
×
1638
                }
1639

UNCOV
1640
                seqNrs, err := fetchSequenceNumbers(bucket)
×
UNCOV
1641
                if err != nil {
×
1642
                        return err
×
1643
                }
×
1644

UNCOV
1645
                if err := payments.DeleteNestedBucket(paymentHash[:]); err != nil {
×
1646
                        return err
×
1647
                }
×
1648

UNCOV
1649
                indexBucket := tx.ReadWriteBucket(paymentsIndexBucket)
×
UNCOV
1650
                for _, k := range seqNrs {
×
UNCOV
1651
                        if err := indexBucket.Delete(k); err != nil {
×
1652
                                return err
×
1653
                        }
×
1654
                }
1655

UNCOV
1656
                return nil
×
UNCOV
1657
        }, func() {})
×
1658
}
1659

1660
// DeletePayments deletes all completed and failed payments from the DB. If
1661
// failedOnly is set, only failed payments will be considered for deletion. If
1662
// failedHtlcsOnly is set, the payment itself won't be deleted, only failed HTLC
1663
// attempts. The method returns the number of deleted payments, which is always
1664
// 0 if failedHtlcsOnly is set.
1665
func (d *DB) DeletePayments(failedOnly, failedHtlcsOnly bool) (int, error) {
2✔
1666
        var numPayments int
2✔
1667
        err := kvdb.Update(d, func(tx kvdb.RwTx) error {
4✔
1668
                payments := tx.ReadWriteBucket(paymentsRootBucket)
2✔
1669
                if payments == nil {
2✔
1670
                        return nil
×
1671
                }
×
1672

1673
                var (
2✔
1674
                        // deleteBuckets is the set of payment buckets we need
2✔
1675
                        // to delete.
2✔
1676
                        deleteBuckets [][]byte
2✔
1677

2✔
1678
                        // deleteIndexes is the set of indexes pointing to these
2✔
1679
                        // payments that need to be deleted.
2✔
1680
                        deleteIndexes [][]byte
2✔
1681

2✔
1682
                        // deleteHtlcs maps a payment hash to the HTLC IDs we
2✔
1683
                        // want to delete for that payment.
2✔
1684
                        deleteHtlcs = make(map[lntypes.Hash][][]byte)
2✔
1685
                )
2✔
1686
                err := payments.ForEach(func(k, _ []byte) error {
4✔
1687
                        bucket := payments.NestedReadBucket(k)
2✔
1688
                        if bucket == nil {
2✔
1689
                                // We only expect sub-buckets to be found in
×
1690
                                // this top-level bucket.
×
1691
                                return fmt.Errorf("non bucket element in " +
×
1692
                                        "payments bucket")
×
1693
                        }
×
1694

1695
                        // If the status is InFlight, we cannot safely delete
1696
                        // the payment information, so we return early.
1697
                        paymentStatus, err := fetchPaymentStatus(bucket)
2✔
1698
                        if err != nil {
2✔
1699
                                return err
×
1700
                        }
×
1701

1702
                        // If the payment has inflight HTLCs, we cannot safely
1703
                        // delete the payment information, so we return an nil
1704
                        // to skip it.
1705
                        if err := paymentStatus.removable(); err != nil {
2✔
UNCOV
1706
                                return nil
×
UNCOV
1707
                        }
×
1708

1709
                        // If we requested to only delete failed payments, we
1710
                        // can return if this one is not.
1711
                        if failedOnly && paymentStatus != StatusFailed {
2✔
UNCOV
1712
                                return nil
×
UNCOV
1713
                        }
×
1714

1715
                        // If we are only deleting failed HTLCs, fetch them.
1716
                        if failedHtlcsOnly {
2✔
UNCOV
1717
                                toDelete, err := fetchFailedHtlcKeys(bucket)
×
UNCOV
1718
                                if err != nil {
×
1719
                                        return err
×
1720
                                }
×
1721

UNCOV
1722
                                hash, err := lntypes.MakeHash(k)
×
UNCOV
1723
                                if err != nil {
×
1724
                                        return err
×
1725
                                }
×
1726

UNCOV
1727
                                deleteHtlcs[hash] = toDelete
×
UNCOV
1728

×
UNCOV
1729
                                // We return, we are only deleting attempts.
×
UNCOV
1730
                                return nil
×
1731
                        }
1732

1733
                        // Add the bucket to the set of buckets we can delete.
1734
                        deleteBuckets = append(deleteBuckets, k)
2✔
1735

2✔
1736
                        // Get all the sequence number associated with the
2✔
1737
                        // payment, including duplicates.
2✔
1738
                        seqNrs, err := fetchSequenceNumbers(bucket)
2✔
1739
                        if err != nil {
2✔
1740
                                return err
×
1741
                        }
×
1742

1743
                        deleteIndexes = append(deleteIndexes, seqNrs...)
2✔
1744
                        numPayments++
2✔
1745
                        return nil
2✔
1746
                })
1747
                if err != nil {
2✔
1748
                        return err
×
1749
                }
×
1750

1751
                // Delete the failed HTLC attempts we found.
1752
                for hash, htlcIDs := range deleteHtlcs {
2✔
UNCOV
1753
                        bucket := payments.NestedReadWriteBucket(hash[:])
×
UNCOV
1754
                        htlcsBucket := bucket.NestedReadWriteBucket(
×
UNCOV
1755
                                paymentHtlcsBucket,
×
UNCOV
1756
                        )
×
UNCOV
1757

×
UNCOV
1758
                        for _, aid := range htlcIDs {
×
UNCOV
1759
                                if err := htlcsBucket.Delete(
×
UNCOV
1760
                                        htlcBucketKey(htlcAttemptInfoKey, aid),
×
UNCOV
1761
                                ); err != nil {
×
1762
                                        return err
×
1763
                                }
×
1764

UNCOV
1765
                                if err := htlcsBucket.Delete(
×
UNCOV
1766
                                        htlcBucketKey(htlcFailInfoKey, aid),
×
UNCOV
1767
                                ); err != nil {
×
1768
                                        return err
×
1769
                                }
×
1770

UNCOV
1771
                                if err := htlcsBucket.Delete(
×
UNCOV
1772
                                        htlcBucketKey(htlcSettleInfoKey, aid),
×
UNCOV
1773
                                ); err != nil {
×
1774
                                        return err
×
1775
                                }
×
1776
                        }
1777
                }
1778

1779
                for _, k := range deleteBuckets {
4✔
1780
                        if err := payments.DeleteNestedBucket(k); err != nil {
2✔
1781
                                return err
×
1782
                        }
×
1783
                }
1784

1785
                // Get our index bucket and delete all indexes pointing to the
1786
                // payments we are deleting.
1787
                indexBucket := tx.ReadWriteBucket(paymentsIndexBucket)
2✔
1788
                for _, k := range deleteIndexes {
4✔
1789
                        if err := indexBucket.Delete(k); err != nil {
2✔
1790
                                return err
×
1791
                        }
×
1792
                }
1793

1794
                return nil
2✔
1795
        }, func() {
2✔
1796
                numPayments = 0
2✔
1797
        })
2✔
1798
        if err != nil {
2✔
1799
                return 0, err
×
1800
        }
×
1801

1802
        return numPayments, nil
2✔
1803
}
1804

1805
// fetchSequenceNumbers fetches all the sequence numbers associated with a
1806
// payment, including those belonging to any duplicate payments.
1807
func fetchSequenceNumbers(paymentBucket kvdb.RBucket) ([][]byte, error) {
2✔
1808
        seqNum := paymentBucket.Get(paymentSequenceKey)
2✔
1809
        if seqNum == nil {
2✔
1810
                return nil, errors.New("expected sequence number")
×
1811
        }
×
1812

1813
        sequenceNumbers := [][]byte{seqNum}
2✔
1814

2✔
1815
        // Get the duplicate payments bucket, if it has no duplicates, just
2✔
1816
        // return early with the payment sequence number.
2✔
1817
        duplicates := paymentBucket.NestedReadBucket(duplicatePaymentsBucket)
2✔
1818
        if duplicates == nil {
4✔
1819
                return sequenceNumbers, nil
2✔
1820
        }
2✔
1821

1822
        // If we do have duplicated, they are keyed by sequence number, so we
1823
        // iterate through the duplicates bucket and add them to our set of
1824
        // sequence numbers.
UNCOV
1825
        if err := duplicates.ForEach(func(k, v []byte) error {
×
UNCOV
1826
                sequenceNumbers = append(sequenceNumbers, k)
×
UNCOV
1827
                return nil
×
UNCOV
1828
        }); err != nil {
×
1829
                return nil, err
×
1830
        }
×
1831

UNCOV
1832
        return sequenceNumbers, nil
×
1833
}
1834

1835
// nolint: dupl
1836
func serializePaymentCreationInfo(w io.Writer, c *PaymentCreationInfo) error {
2✔
1837
        var scratch [8]byte
2✔
1838

2✔
1839
        if _, err := w.Write(c.PaymentIdentifier[:]); err != nil {
2✔
1840
                return err
×
1841
        }
×
1842

1843
        byteOrder.PutUint64(scratch[:], uint64(c.Value))
2✔
1844
        if _, err := w.Write(scratch[:]); err != nil {
2✔
1845
                return err
×
1846
        }
×
1847

1848
        if err := serializeTime(w, c.CreationTime); err != nil {
2✔
1849
                return err
×
1850
        }
×
1851

1852
        byteOrder.PutUint32(scratch[:4], uint32(len(c.PaymentRequest)))
2✔
1853
        if _, err := w.Write(scratch[:4]); err != nil {
2✔
1854
                return err
×
1855
        }
×
1856

1857
        if _, err := w.Write(c.PaymentRequest[:]); err != nil {
2✔
1858
                return err
×
1859
        }
×
1860

1861
        // Any remaining bytes are TLV encoded records. Currently, these are
1862
        // only the custom records provided by the user to be sent to the first
1863
        // hop. But this can easily be extended with further records by merging
1864
        // the records into a single TLV stream.
1865
        err := c.FirstHopCustomRecords.SerializeTo(w)
2✔
1866
        if err != nil {
2✔
1867
                return err
×
1868
        }
×
1869

1870
        return nil
2✔
1871
}
1872

1873
func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo,
1874
        error) {
2✔
1875

2✔
1876
        var scratch [8]byte
2✔
1877

2✔
1878
        c := &PaymentCreationInfo{}
2✔
1879

2✔
1880
        if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil {
2✔
1881
                return nil, err
×
1882
        }
×
1883

1884
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
2✔
1885
                return nil, err
×
1886
        }
×
1887
        c.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
2✔
1888

2✔
1889
        creationTime, err := deserializeTime(r)
2✔
1890
        if err != nil {
2✔
1891
                return nil, err
×
1892
        }
×
1893
        c.CreationTime = creationTime
2✔
1894

2✔
1895
        if _, err := io.ReadFull(r, scratch[:4]); err != nil {
2✔
1896
                return nil, err
×
1897
        }
×
1898

1899
        reqLen := uint32(byteOrder.Uint32(scratch[:4]))
2✔
1900
        payReq := make([]byte, reqLen)
2✔
1901
        if reqLen > 0 {
4✔
1902
                if _, err := io.ReadFull(r, payReq); err != nil {
2✔
1903
                        return nil, err
×
1904
                }
×
1905
        }
1906
        c.PaymentRequest = payReq
2✔
1907

2✔
1908
        // Any remaining bytes are TLV encoded records. Currently, these are
2✔
1909
        // only the custom records provided by the user to be sent to the first
2✔
1910
        // hop. But this can easily be extended with further records by merging
2✔
1911
        // the records into a single TLV stream.
2✔
1912
        c.FirstHopCustomRecords, err = lnwire.ParseCustomRecordsFrom(r)
2✔
1913
        if err != nil {
2✔
1914
                return nil, err
×
1915
        }
×
1916

1917
        return c, nil
2✔
1918
}
1919

1920
func serializeHTLCAttemptInfo(w io.Writer, a *HTLCAttemptInfo) error {
2✔
1921
        if err := WriteElements(w, a.sessionKey); err != nil {
2✔
1922
                return err
×
1923
        }
×
1924

1925
        if err := SerializeRoute(w, a.Route); err != nil {
2✔
1926
                return err
×
1927
        }
×
1928

1929
        if err := serializeTime(w, a.AttemptTime); err != nil {
2✔
1930
                return err
×
1931
        }
×
1932

1933
        // If the hash is nil we can just return.
1934
        if a.Hash == nil {
2✔
1935
                return nil
×
1936
        }
×
1937

1938
        if _, err := w.Write(a.Hash[:]); err != nil {
2✔
1939
                return err
×
1940
        }
×
1941

1942
        // Merge the fixed/known records together with the custom records to
1943
        // serialize them as a single blob. We can't do this in SerializeRoute
1944
        // because we're in the middle of the byte stream there. We can only do
1945
        // TLV serialization at the end of the stream, since EOF is allowed for
1946
        // a stream if no more data is expected.
1947
        producers := []tlv.RecordProducer{
2✔
1948
                &a.Route.FirstHopAmount,
2✔
1949
        }
2✔
1950
        tlvData, err := lnwire.MergeAndEncode(
2✔
1951
                producers, nil, a.Route.FirstHopWireCustomRecords,
2✔
1952
        )
2✔
1953
        if err != nil {
2✔
1954
                return err
×
1955
        }
×
1956

1957
        if _, err := w.Write(tlvData); err != nil {
2✔
1958
                return err
×
1959
        }
×
1960

1961
        return nil
2✔
1962
}
1963

1964
func deserializeHTLCAttemptInfo(r io.Reader) (*HTLCAttemptInfo, error) {
2✔
1965
        a := &HTLCAttemptInfo{}
2✔
1966
        err := ReadElements(r, &a.sessionKey)
2✔
1967
        if err != nil {
2✔
1968
                return nil, err
×
1969
        }
×
1970

1971
        a.Route, err = DeserializeRoute(r)
2✔
1972
        if err != nil {
2✔
1973
                return nil, err
×
1974
        }
×
1975

1976
        a.AttemptTime, err = deserializeTime(r)
2✔
1977
        if err != nil {
2✔
1978
                return nil, err
×
1979
        }
×
1980

1981
        hash := lntypes.Hash{}
2✔
1982
        _, err = io.ReadFull(r, hash[:])
2✔
1983

2✔
1984
        switch {
2✔
1985
        // Older payment attempts wouldn't have the hash set, in which case we
1986
        // can just return.
1987
        case err == io.EOF, err == io.ErrUnexpectedEOF:
×
1988
                return a, nil
×
1989

1990
        case err != nil:
×
1991
                return nil, err
×
1992

1993
        default:
2✔
1994
        }
1995

1996
        a.Hash = &hash
2✔
1997

2✔
1998
        // Read any remaining data (if any) and parse it into the known records
2✔
1999
        // and custom records.
2✔
2000
        extraData, err := io.ReadAll(r)
2✔
2001
        if err != nil {
2✔
2002
                return nil, err
×
2003
        }
×
2004

2005
        customRecords, _, _, err := lnwire.ParseAndExtractCustomRecords(
2✔
2006
                extraData, &a.Route.FirstHopAmount,
2✔
2007
        )
2✔
2008
        if err != nil {
2✔
2009
                return nil, err
×
2010
        }
×
2011

2012
        a.Route.FirstHopWireCustomRecords = customRecords
2✔
2013

2✔
2014
        return a, nil
2✔
2015
}
2016

2017
func serializeHop(w io.Writer, h *route.Hop) error {
2✔
2018
        if err := WriteElements(w,
2✔
2019
                h.PubKeyBytes[:],
2✔
2020
                h.ChannelID,
2✔
2021
                h.OutgoingTimeLock,
2✔
2022
                h.AmtToForward,
2✔
2023
        ); err != nil {
2✔
2024
                return err
×
2025
        }
×
2026

2027
        if err := binary.Write(w, byteOrder, h.LegacyPayload); err != nil {
2✔
2028
                return err
×
2029
        }
×
2030

2031
        // For legacy payloads, we don't need to write any TLV records, so
2032
        // we'll write a zero indicating the our serialized TLV map has no
2033
        // records.
2034
        if h.LegacyPayload {
2✔
UNCOV
2035
                return WriteElements(w, uint32(0))
×
UNCOV
2036
        }
×
2037

2038
        // Gather all non-primitive TLV records so that they can be serialized
2039
        // as a single blob.
2040
        //
2041
        // TODO(conner): add migration to unify all fields in a single TLV
2042
        // blobs. The split approach will cause headaches down the road as more
2043
        // fields are added, which we can avoid by having a single TLV stream
2044
        // for all payload fields.
2045
        var records []tlv.Record
2✔
2046
        if h.MPP != nil {
4✔
2047
                records = append(records, h.MPP.Record())
2✔
2048
        }
2✔
2049

2050
        // Add blinding point and encrypted data if present.
2051
        if h.EncryptedData != nil {
4✔
2052
                records = append(records, record.NewEncryptedDataRecord(
2✔
2053
                        &h.EncryptedData,
2✔
2054
                ))
2✔
2055
        }
2✔
2056

2057
        if h.BlindingPoint != nil {
4✔
2058
                records = append(records, record.NewBlindingPointRecord(
2✔
2059
                        &h.BlindingPoint,
2✔
2060
                ))
2✔
2061
        }
2✔
2062

2063
        if h.AMP != nil {
4✔
2064
                records = append(records, h.AMP.Record())
2✔
2065
        }
2✔
2066

2067
        if h.Metadata != nil {
2✔
UNCOV
2068
                records = append(records, record.NewMetadataRecord(&h.Metadata))
×
UNCOV
2069
        }
×
2070

2071
        if h.TotalAmtMsat != 0 {
4✔
2072
                totalMsatInt := uint64(h.TotalAmtMsat)
2✔
2073
                records = append(
2✔
2074
                        records, record.NewTotalAmtMsatBlinded(&totalMsatInt),
2✔
2075
                )
2✔
2076
        }
2✔
2077

2078
        // Final sanity check to absolutely rule out custom records that are not
2079
        // custom and write into the standard range.
2080
        if err := h.CustomRecords.Validate(); err != nil {
2✔
2081
                return err
×
2082
        }
×
2083

2084
        // Convert custom records to tlv and add to the record list.
2085
        // MapToRecords sorts the list, so adding it here will keep the list
2086
        // canonical.
2087
        tlvRecords := tlv.MapToRecords(h.CustomRecords)
2✔
2088
        records = append(records, tlvRecords...)
2✔
2089

2✔
2090
        // Otherwise, we'll transform our slice of records into a map of the
2✔
2091
        // raw bytes, then serialize them in-line with a length (number of
2✔
2092
        // elements) prefix.
2✔
2093
        mapRecords, err := tlv.RecordsToMap(records)
2✔
2094
        if err != nil {
2✔
2095
                return err
×
2096
        }
×
2097

2098
        numRecords := uint32(len(mapRecords))
2✔
2099
        if err := WriteElements(w, numRecords); err != nil {
2✔
2100
                return err
×
2101
        }
×
2102

2103
        for recordType, rawBytes := range mapRecords {
4✔
2104
                if err := WriteElements(w, recordType); err != nil {
2✔
2105
                        return err
×
2106
                }
×
2107

2108
                if err := wire.WriteVarBytes(w, 0, rawBytes); err != nil {
2✔
2109
                        return err
×
2110
                }
×
2111
        }
2112

2113
        return nil
2✔
2114
}
2115

2116
// maxOnionPayloadSize is the largest Sphinx payload possible, so we don't need
2117
// to read/write a TLV stream larger than this.
2118
const maxOnionPayloadSize = 1300
2119

2120
func deserializeHop(r io.Reader) (*route.Hop, error) {
2✔
2121
        h := &route.Hop{}
2✔
2122

2✔
2123
        var pub []byte
2✔
2124
        if err := ReadElements(r, &pub); err != nil {
2✔
2125
                return nil, err
×
2126
        }
×
2127
        copy(h.PubKeyBytes[:], pub)
2✔
2128

2✔
2129
        if err := ReadElements(r,
2✔
2130
                &h.ChannelID, &h.OutgoingTimeLock, &h.AmtToForward,
2✔
2131
        ); err != nil {
2✔
2132
                return nil, err
×
2133
        }
×
2134

2135
        // TODO(roasbeef): change field to allow LegacyPayload false to be the
2136
        // legacy default?
2137
        err := binary.Read(r, byteOrder, &h.LegacyPayload)
2✔
2138
        if err != nil {
2✔
2139
                return nil, err
×
2140
        }
×
2141

2142
        var numElements uint32
2✔
2143
        if err := ReadElements(r, &numElements); err != nil {
2✔
2144
                return nil, err
×
2145
        }
×
2146

2147
        // If there're no elements, then we can return early.
2148
        if numElements == 0 {
4✔
2149
                return h, nil
2✔
2150
        }
2✔
2151

2152
        tlvMap := make(map[uint64][]byte)
2✔
2153
        for i := uint32(0); i < numElements; i++ {
4✔
2154
                var tlvType uint64
2✔
2155
                if err := ReadElements(r, &tlvType); err != nil {
2✔
2156
                        return nil, err
×
2157
                }
×
2158

2159
                rawRecordBytes, err := wire.ReadVarBytes(
2✔
2160
                        r, 0, maxOnionPayloadSize, "tlv",
2✔
2161
                )
2✔
2162
                if err != nil {
2✔
2163
                        return nil, err
×
2164
                }
×
2165

2166
                tlvMap[tlvType] = rawRecordBytes
2✔
2167
        }
2168

2169
        // If the MPP type is present, remove it from the generic TLV map and
2170
        // parse it back into a proper MPP struct.
2171
        //
2172
        // TODO(conner): add migration to unify all fields in a single TLV
2173
        // blobs. The split approach will cause headaches down the road as more
2174
        // fields are added, which we can avoid by having a single TLV stream
2175
        // for all payload fields.
2176
        mppType := uint64(record.MPPOnionType)
2✔
2177
        if mppBytes, ok := tlvMap[mppType]; ok {
4✔
2178
                delete(tlvMap, mppType)
2✔
2179

2✔
2180
                var (
2✔
2181
                        mpp    = &record.MPP{}
2✔
2182
                        mppRec = mpp.Record()
2✔
2183
                        r      = bytes.NewReader(mppBytes)
2✔
2184
                )
2✔
2185
                err := mppRec.Decode(r, uint64(len(mppBytes)))
2✔
2186
                if err != nil {
2✔
2187
                        return nil, err
×
2188
                }
×
2189
                h.MPP = mpp
2✔
2190
        }
2191

2192
        // If encrypted data or blinding key are present, remove them from
2193
        // the TLV map and parse into proper types.
2194
        encryptedDataType := uint64(record.EncryptedDataOnionType)
2✔
2195
        if data, ok := tlvMap[encryptedDataType]; ok {
4✔
2196
                delete(tlvMap, encryptedDataType)
2✔
2197
                h.EncryptedData = data
2✔
2198
        }
2✔
2199

2200
        blindingType := uint64(record.BlindingPointOnionType)
2✔
2201
        if blindingPoint, ok := tlvMap[blindingType]; ok {
4✔
2202
                delete(tlvMap, blindingType)
2✔
2203

2✔
2204
                h.BlindingPoint, err = btcec.ParsePubKey(blindingPoint)
2✔
2205
                if err != nil {
2✔
2206
                        return nil, fmt.Errorf("invalid blinding point: %w",
×
2207
                                err)
×
2208
                }
×
2209
        }
2210

2211
        ampType := uint64(record.AMPOnionType)
2✔
2212
        if ampBytes, ok := tlvMap[ampType]; ok {
4✔
2213
                delete(tlvMap, ampType)
2✔
2214

2✔
2215
                var (
2✔
2216
                        amp    = &record.AMP{}
2✔
2217
                        ampRec = amp.Record()
2✔
2218
                        r      = bytes.NewReader(ampBytes)
2✔
2219
                )
2✔
2220
                err := ampRec.Decode(r, uint64(len(ampBytes)))
2✔
2221
                if err != nil {
2✔
2222
                        return nil, err
×
2223
                }
×
2224
                h.AMP = amp
2✔
2225
        }
2226

2227
        // If the metadata type is present, remove it from the tlv map and
2228
        // populate directly on the hop.
2229
        metadataType := uint64(record.MetadataOnionType)
2✔
2230
        if metadata, ok := tlvMap[metadataType]; ok {
2✔
UNCOV
2231
                delete(tlvMap, metadataType)
×
UNCOV
2232

×
UNCOV
2233
                h.Metadata = metadata
×
UNCOV
2234
        }
×
2235

2236
        totalAmtMsatType := uint64(record.TotalAmtMsatBlindedType)
2✔
2237
        if totalAmtMsat, ok := tlvMap[totalAmtMsatType]; ok {
4✔
2238
                delete(tlvMap, totalAmtMsatType)
2✔
2239

2✔
2240
                var (
2✔
2241
                        totalAmtMsatInt uint64
2✔
2242
                        buf             [8]byte
2✔
2243
                )
2✔
2244
                if err := tlv.DTUint64(
2✔
2245
                        bytes.NewReader(totalAmtMsat),
2✔
2246
                        &totalAmtMsatInt,
2✔
2247
                        &buf,
2✔
2248
                        uint64(len(totalAmtMsat)),
2✔
2249
                ); err != nil {
2✔
2250
                        return nil, err
×
2251
                }
×
2252

2253
                h.TotalAmtMsat = lnwire.MilliSatoshi(totalAmtMsatInt)
2✔
2254
        }
2255

2256
        h.CustomRecords = tlvMap
2✔
2257

2✔
2258
        return h, nil
2✔
2259
}
2260

2261
// SerializeRoute serializes a route.
2262
func SerializeRoute(w io.Writer, r route.Route) error {
2✔
2263
        if err := WriteElements(w,
2✔
2264
                r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
2✔
2265
        ); err != nil {
2✔
2266
                return err
×
2267
        }
×
2268

2269
        if err := WriteElements(w, uint32(len(r.Hops))); err != nil {
2✔
2270
                return err
×
2271
        }
×
2272

2273
        for _, h := range r.Hops {
4✔
2274
                if err := serializeHop(w, h); err != nil {
2✔
2275
                        return err
×
2276
                }
×
2277
        }
2278

2279
        // Any new/extra TLV data is encoded in serializeHTLCAttemptInfo!
2280

2281
        return nil
2✔
2282
}
2283

2284
// DeserializeRoute deserializes a route.
2285
func DeserializeRoute(r io.Reader) (route.Route, error) {
2✔
2286
        rt := route.Route{}
2✔
2287
        if err := ReadElements(r,
2✔
2288
                &rt.TotalTimeLock, &rt.TotalAmount,
2✔
2289
        ); err != nil {
2✔
2290
                return rt, err
×
2291
        }
×
2292

2293
        var pub []byte
2✔
2294
        if err := ReadElements(r, &pub); err != nil {
2✔
2295
                return rt, err
×
2296
        }
×
2297
        copy(rt.SourcePubKey[:], pub)
2✔
2298

2✔
2299
        var numHops uint32
2✔
2300
        if err := ReadElements(r, &numHops); err != nil {
2✔
2301
                return rt, err
×
2302
        }
×
2303

2304
        var hops []*route.Hop
2✔
2305
        for i := uint32(0); i < numHops; i++ {
4✔
2306
                hop, err := deserializeHop(r)
2✔
2307
                if err != nil {
2✔
2308
                        return rt, err
×
2309
                }
×
2310
                hops = append(hops, hop)
2✔
2311
        }
2312
        rt.Hops = hops
2✔
2313

2✔
2314
        // Any new/extra TLV data is decoded in deserializeHTLCAttemptInfo!
2✔
2315

2✔
2316
        return rt, nil
2✔
2317
}
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