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

lightningnetwork / lnd / 15111492333

19 May 2025 11:13AM UTC coverage: 58.565% (-10.4%) from 68.99%
15111492333

Pull #9825

github

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

324 of 467 new or added lines in 4 files covered. (69.38%)

28227 existing lines in 451 files now uncovered.

97388 of 166290 relevant lines covered (58.57%)

1.82 hits per line

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

59.11
/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 {
3✔
280
        return r.String()
3✔
281
}
3✔
282

283
// String returns a human-readable FailureReason.
284
func (r FailureReason) String() string {
3✔
285
        switch r {
3✔
UNCOV
286
        case FailureReasonTimeout:
×
UNCOV
287
                return "timeout"
×
288
        case FailureReasonNoRoute:
3✔
289
                return "no_route"
3✔
UNCOV
290
        case FailureReasonError:
×
UNCOV
291
                return "error"
×
292
        case FailureReasonPaymentDetails:
3✔
293
                return "incorrect_payment_details"
3✔
294
        case FailureReasonInsufficientBalance:
3✔
295
                return "insufficient_balance"
3✔
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 {
3✔
313
        return &KVPaymentsDB{
3✔
314
                db: db,
3✔
315
        }
3✔
316
}
3✔
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 {
3✔
324

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

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

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

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

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

3✔
354
                switch {
3✔
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:
3✔
359
                        if err := paymentStatus.initializable(); err != nil {
6✔
360
                                updateErr = err
3✔
361
                                return nil
3✔
362
                        }
3✔
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)
3✔
376
                if seqBytes != nil {
6✔
377
                        indexBucket := tx.ReadWriteBucket(paymentsIndexBucket)
3✔
378
                        if err := indexBucket.Delete(seqBytes); err != nil {
3✔
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(
3✔
387
                        tx, sequenceNum, info.PaymentIdentifier,
3✔
388
                )
3✔
389
                if err != nil {
3✔
NEW
390
                        return err
×
NEW
391
                }
×
392

393
                err = bucket.Put(paymentSequenceKey, sequenceNum)
3✔
394
                if err != nil {
3✔
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)
3✔
401
                if err != nil {
3✔
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)
3✔
409
                if err != nil && err != kvdb.ErrBucketNotFound {
3✔
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)
3✔
416
        })
417
        if err != nil {
3✔
NEW
418
                return fmt.Errorf("unable to init payment: %w", err)
×
NEW
419
        }
×
420

421
        return updateErr
3✔
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 {
3✔
427
        if !p.db.keepFailedPaymentAttempts {
3✔
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
        return nil
3✔
435
}
436

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

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

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

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

456
        indexes := tx.ReadWriteBucket(paymentsIndexBucket)
3✔
457
        return indexes.Put(sequenceNumber, b.Bytes())
3✔
458
}
459

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

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

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

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

486
        return hash, nil
3✔
487
}
488

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

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

3✔
502
        htlcIDBytes := make([]byte, 8)
3✔
503
        binary.BigEndian.PutUint64(htlcIDBytes, attempt.AttemptID)
3✔
504

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

513
                payment, err = fetchPayment(bucket)
3✔
514
                if err != nil {
3✔
NEW
515
                        return err
×
NEW
516
                }
×
517

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

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

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

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

541
                for _, h := range payment.InFlightHTLCs() {
6✔
542
                        hMpp := h.Route.FinalHop().MPP
3✔
543

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

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

×
NEW
558
                                        //nolint:ll
×
NEW
559
                                        return ErrBlindedPaymentTotalAmountMismatch
×
NEW
560
                                }
×
561

562
                                continue
3✔
563
                        }
564

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

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

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

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

586
                        if mpp.TotalMsat() != hMpp.TotalMsat() {
3✔
NEW
587
                                return ErrMPPTotalAmountMismatch
×
NEW
588
                        }
×
589
                }
590

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

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

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

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

622
                // Retrieve attempt info for the notification.
623
                payment, err = fetchPayment(bucket)
3✔
624
                return err
3✔
625
        })
626
        if err != nil {
3✔
NEW
627
                return nil, err
×
NEW
628
        }
×
629

630
        return payment, err
3✔
631
}
632

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

3✔
643
        var b bytes.Buffer
3✔
644
        if err := serializeHTLCSettleInfo(&b, settleInfo); err != nil {
3✔
NEW
645
                return nil, err
×
NEW
646
        }
×
647
        settleBytes := b.Bytes()
3✔
648

3✔
649
        return p.updateHtlcKey(hash, attemptID, htlcSettleInfoKey, settleBytes)
3✔
650
}
651

652
// FailAttempt marks the given payment attempt failed.
653
func (p *KVPaymentsDB) FailAttempt(hash lntypes.Hash,
654
        attemptID uint64, failInfo *HTLCFailInfo) (*MPPayment, error) {
3✔
655

3✔
656
        var b bytes.Buffer
3✔
657
        if err := serializeHTLCFailInfo(&b, failInfo); err != nil {
3✔
NEW
658
                return nil, err
×
NEW
659
        }
×
660
        failBytes := b.Bytes()
3✔
661

3✔
662
        return p.updateHtlcKey(hash, attemptID, htlcFailInfoKey, failBytes)
3✔
663
}
664

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

3✔
669
        aid := make([]byte, 8)
3✔
670
        binary.BigEndian.PutUint64(aid, attemptID)
3✔
671

3✔
672
        var payment *MPPayment
3✔
673
        err := kvdb.Batch(p.db.Backend, func(tx kvdb.RwTx) error {
6✔
674
                payment = nil
3✔
675

3✔
676
                prefetchPayment(tx, paymentHash)
3✔
677
                bucket, err := fetchPaymentBucketUpdate(tx, paymentHash)
3✔
678
                if err != nil {
3✔
NEW
679
                        return err
×
NEW
680
                }
×
681

682
                p, err := fetchPayment(bucket)
3✔
683
                if err != nil {
3✔
NEW
684
                        return err
×
NEW
685
                }
×
686

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

694
                htlcsBucket := bucket.NestedReadWriteBucket(paymentHtlcsBucket)
3✔
695
                if htlcsBucket == nil {
3✔
NEW
696
                        return fmt.Errorf("htlcs bucket not found")
×
NEW
697
                }
×
698

699
                if htlcsBucket.Get(htlcBucketKey(htlcAttemptInfoKey, aid)) == nil {
3✔
NEW
700
                        return fmt.Errorf("HTLC with ID %v not registered",
×
NEW
701
                                attemptID)
×
NEW
702
                }
×
703

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

709
                if htlcsBucket.Get(htlcBucketKey(htlcSettleInfoKey, aid)) != nil {
3✔
NEW
710
                        return ErrAttemptAlreadySettled
×
NEW
711
                }
×
712

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

719
                // Retrieve attempt info for the notification.
720
                payment, err = fetchPayment(bucket)
3✔
721
                return err
3✔
722
        })
723
        if err != nil {
3✔
NEW
724
                return nil, err
×
NEW
725
        }
×
726

727
        return payment, err
3✔
728
}
729

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

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

3✔
747
                prefetchPayment(tx, paymentHash)
3✔
748
                bucket, err := fetchPaymentBucketUpdate(tx, paymentHash)
3✔
749
                if err == ErrPaymentNotInitiated {
3✔
NEW
750
                        updateErr = ErrPaymentNotInitiated
×
NEW
751
                        return nil
×
752
                } else if err != nil {
3✔
NEW
753
                        return err
×
NEW
754
                }
×
755

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

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

775
                // Retrieve attempt info for the notification, if available.
776
                payment, err = fetchPayment(bucket)
3✔
777
                if err != nil {
3✔
NEW
778
                        return err
×
NEW
779
                }
×
780

781
                return nil
3✔
782
        })
783
        if err != nil {
3✔
NEW
784
                return nil, err
×
NEW
785
        }
×
786

787
        return payment, updateErr
3✔
788
}
789

790
// FetchPayment returns information about a payment from the database.
791
func (p *KVPaymentsDB) FetchPayment(paymentHash lntypes.Hash) (
792
        *MPPayment, error) {
3✔
793

3✔
794
        var payment *MPPayment
3✔
795
        err := kvdb.View(p.db, func(tx kvdb.RTx) error {
6✔
796
                prefetchPayment(tx, paymentHash)
3✔
797
                bucket, err := fetchPaymentBucket(tx, paymentHash)
3✔
798
                if err != nil {
3✔
NEW
799
                        return err
×
NEW
800
                }
×
801

802
                payment, err = fetchPayment(bucket)
3✔
803

3✔
804
                return err
3✔
805
        }, func() {
3✔
806
                payment = nil
3✔
807
        })
3✔
808
        if err != nil {
3✔
NEW
809
                return nil, err
×
NEW
810
        }
×
811

812
        return payment, nil
3✔
813
}
814

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

835
// createPaymentBucket creates or fetches the sub-bucket assigned to this
836
// payment hash.
837
func createPaymentBucket(tx kvdb.RwTx, paymentHash lntypes.Hash) (
838
        kvdb.RwBucket, error) {
3✔
839

3✔
840
        payments, err := tx.CreateTopLevelBucket(paymentsRootBucket)
3✔
841
        if err != nil {
3✔
NEW
842
                return nil, err
×
NEW
843
        }
×
844

845
        return payments.CreateBucketIfNotExists(paymentHash[:])
3✔
846
}
847

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

3✔
853
        payments := tx.ReadBucket(paymentsRootBucket)
3✔
854
        if payments == nil {
3✔
NEW
855
                return nil, ErrPaymentNotInitiated
×
NEW
856
        }
×
857

858
        bucket := payments.NestedReadBucket(paymentHash[:])
3✔
859
        if bucket == nil {
3✔
NEW
860
                return nil, ErrPaymentNotInitiated
×
NEW
861
        }
×
862

863
        return bucket, nil
3✔
864

865
}
866

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

3✔
872
        payments := tx.ReadWriteBucket(paymentsRootBucket)
3✔
873
        if payments == nil {
3✔
NEW
874
                return nil, ErrPaymentNotInitiated
×
NEW
875
        }
×
876

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

882
        return bucket, nil
3✔
883
}
884

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

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

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

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

919
                p.storedPaymentSeq = newUpperBound
3✔
920
        }
921

922
        p.currPaymentSeq++
3✔
923
        b := make([]byte, 8)
3✔
924
        binary.BigEndian.PutUint64(b, p.currPaymentSeq)
3✔
925

3✔
926
        return b, nil
3✔
927
}
928

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

938
        payment, err := fetchPayment(bucket)
3✔
939
        if err != nil {
3✔
NEW
940
                return 0, err
×
NEW
941
        }
×
942

943
        return payment.Status, nil
3✔
944
}
945

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

3✔
955
        err := kvdb.View(p.db, func(tx kvdb.RTx) error {
6✔
956
                payments := tx.ReadBucket(paymentsRootBucket)
3✔
957
                if payments == nil {
6✔
958
                        return nil
3✔
959
                }
3✔
960

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

967
                        p, err := fetchPayment(bucket)
3✔
968
                        if err != nil {
3✔
NEW
969
                                return err
×
NEW
970
                        }
×
971

972
                        processedCount++
3✔
973
                        if time.Since(lastLogTime) >=
3✔
974
                                paymentProgressLogInterval {
3✔
NEW
975

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

×
NEW
981
                                lastLogTime = time.Now()
×
NEW
982
                        }
×
983

984
                        // Skip the payment if it's terminated.
985
                        if p.Terminated() {
6✔
986
                                return nil
3✔
987
                        }
3✔
988

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

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

3✔
1005
        return inFlights, nil
3✔
1006
}
1007

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

1015
        // Value is the amount we are paying.
1016
        Value lnwire.MilliSatoshi
1017

1018
        // CreationTime is the time when this payment was initiated.
1019
        CreationTime time.Time
1020

1021
        // PaymentRequest is the full payment request, if any.
1022
        PaymentRequest []byte
1023

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

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

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

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

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

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

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

UNCOV
1071
                        payments = append(payments, p)
×
UNCOV
1072

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

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

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

UNCOV
1097
        return payments, nil
×
1098
}
1099

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

1106
        r := bytes.NewReader(b)
3✔
1107
        return deserializePaymentCreationInfo(r)
3✔
1108
}
1109

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

1116
        sequenceNum := binary.BigEndian.Uint64(seqBytes)
3✔
1117

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

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

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

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

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

1155
        return payment, nil
3✔
1156
}
1157

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

3✔
1163
        attemptInfoCount := 0
3✔
1164
        err := bucket.ForEach(func(k, v []byte) error {
6✔
1165
                aid := byteOrder.Uint64(k[len(k)-8:])
3✔
1166

3✔
1167
                if _, ok := htlcsMap[aid]; !ok {
6✔
1168
                        htlcsMap[aid] = &HTLCAttempt{}
3✔
1169
                }
3✔
1170

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

1179
                        attemptInfo.AttemptID = aid
3✔
1180
                        htlcsMap[aid].HTLCAttemptInfo = *attemptInfo
3✔
1181
                        attemptInfoCount++
3✔
1182

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

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

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

1199
                return nil
3✔
1200
        })
1201
        if err != nil {
3✔
1202
                return nil, err
×
1203
        }
×
1204

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

1210
        keys := make([]uint64, len(htlcsMap))
3✔
1211
        i := 0
3✔
1212
        for k := range htlcsMap {
6✔
1213
                keys[i] = k
3✔
1214
                i++
3✔
1215
        }
3✔
1216

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

1224
        htlcs := make([]HTLCAttempt, len(htlcsMap))
3✔
1225
        for i, key := range keys {
6✔
1226
                htlcs[i] = *htlcsMap[key]
3✔
1227
        }
3✔
1228

1229
        return htlcs, nil
3✔
1230
}
1231

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

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

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

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

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

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

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

×
UNCOV
1277
                htlcKeys = append(htlcKeys, htlcKeyBytes)
×
1278
        }
1279

UNCOV
1280
        return htlcKeys, nil
×
1281
}
1282

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

1295
        // MaxPayments is the maximal number of payments returned in the
1296
        // payments query.
1297
        MaxPayments uint64
1298

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

×
UNCOV
1397
                                return false, err
×
UNCOV
1398
                        }
×
1399

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

3✔
1404
                        // Skip any payments that were created before the
3✔
1405
                        // specified time.
3✔
1406
                        if createTime < query.CreationDateStart {
6✔
1407
                                return false, nil
3✔
1408
                        }
3✔
1409

1410
                        // Skip any payments that were created after the
1411
                        // specified time.
1412
                        if query.CreationDateEnd != 0 &&
3✔
1413
                                createTime > query.CreationDateEnd {
6✔
1414

3✔
1415
                                return false, nil
3✔
1416
                        }
3✔
1417

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

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

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

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

×
1447
                                return nil
×
1448
                        }
×
1449

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

1462
                        resp.TotalCount = totalPayments
×
1463
                }
1464

1465
                return nil
3✔
1466
        }, func() {
3✔
1467
                resp = PaymentsResponse{}
3✔
1468
        }); err != nil {
3✔
1469
                return resp, err
×
1470
        }
×
1471

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

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

1488
        return resp, nil
3✔
1489
}
1490

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

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

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

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

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

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

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

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

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

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

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

UNCOV
1565
        return duplicatePayment, nil
×
1566
}
1567

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

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

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

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

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

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

UNCOV
1608
                        htlcsBucket := bucket.NestedReadWriteBucket(
×
UNCOV
1609
                                paymentHtlcsBucket,
×
UNCOV
1610
                        )
×
UNCOV
1611

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

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

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

UNCOV
1635
                        return nil
×
1636
                }
1637

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

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

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

UNCOV
1654
                return nil
×
UNCOV
1655
        }, func() {})
×
1656
}
1657

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

1671
                var (
3✔
1672
                        // deleteBuckets is the set of payment buckets we need
3✔
1673
                        // to delete.
3✔
1674
                        deleteBuckets [][]byte
3✔
1675

3✔
1676
                        // deleteIndexes is the set of indexes pointing to these
3✔
1677
                        // payments that need to be deleted.
3✔
1678
                        deleteIndexes [][]byte
3✔
1679

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

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

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

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

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

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

UNCOV
1725
                                deleteHtlcs[hash] = toDelete
×
UNCOV
1726

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

1731
                        // Add the bucket to the set of buckets we can delete.
1732
                        deleteBuckets = append(deleteBuckets, k)
3✔
1733

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

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

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

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

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

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

1777
                for _, k := range deleteBuckets {
6✔
1778
                        if err := payments.DeleteNestedBucket(k); err != nil {
3✔
1779
                                return err
×
1780
                        }
×
1781
                }
1782

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

1792
                return nil
3✔
1793
        }, func() {
3✔
1794
                numPayments = 0
3✔
1795
        })
3✔
1796
        if err != nil {
3✔
1797
                return 0, err
×
1798
        }
×
1799

1800
        return numPayments, nil
3✔
1801
}
1802

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

1811
        sequenceNumbers := [][]byte{seqNum}
3✔
1812

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

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

UNCOV
1830
        return sequenceNumbers, nil
×
1831
}
1832

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

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

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

1846
        if err := serializeTime(w, c.CreationTime); err != nil {
3✔
1847
                return err
×
1848
        }
×
1849

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

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

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

1868
        return nil
3✔
1869
}
1870

1871
func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo,
1872
        error) {
3✔
1873

3✔
1874
        var scratch [8]byte
3✔
1875

3✔
1876
        c := &PaymentCreationInfo{}
3✔
1877

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

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

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

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

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

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

1915
        return c, nil
3✔
1916
}
1917

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

1923
        if err := SerializeRoute(w, a.Route); err != nil {
3✔
1924
                return err
×
1925
        }
×
1926

1927
        if err := serializeTime(w, a.AttemptTime); err != nil {
3✔
1928
                return err
×
1929
        }
×
1930

1931
        // If the hash is nil we can just return.
1932
        if a.Hash == nil {
3✔
1933
                return nil
×
1934
        }
×
1935

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

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

1955
        if _, err := w.Write(tlvData); err != nil {
3✔
1956
                return err
×
1957
        }
×
1958

1959
        return nil
3✔
1960
}
1961

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

1969
        a.Route, err = DeserializeRoute(r)
3✔
1970
        if err != nil {
3✔
1971
                return nil, err
×
1972
        }
×
1973

1974
        a.AttemptTime, err = deserializeTime(r)
3✔
1975
        if err != nil {
3✔
1976
                return nil, err
×
1977
        }
×
1978

1979
        hash := lntypes.Hash{}
3✔
1980
        _, err = io.ReadFull(r, hash[:])
3✔
1981

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

1988
        case err != nil:
×
1989
                return nil, err
×
1990

1991
        default:
3✔
1992
        }
1993

1994
        a.Hash = &hash
3✔
1995

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

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

2010
        a.Route.FirstHopWireCustomRecords = customRecords
3✔
2011

3✔
2012
        return a, nil
3✔
2013
}
2014

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

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

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

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

2048
        // Add blinding point and encrypted data if present.
2049
        if h.EncryptedData != nil {
6✔
2050
                records = append(records, record.NewEncryptedDataRecord(
3✔
2051
                        &h.EncryptedData,
3✔
2052
                ))
3✔
2053
        }
3✔
2054

2055
        if h.BlindingPoint != nil {
6✔
2056
                records = append(records, record.NewBlindingPointRecord(
3✔
2057
                        &h.BlindingPoint,
3✔
2058
                ))
3✔
2059
        }
3✔
2060

2061
        if h.AMP != nil {
6✔
2062
                records = append(records, h.AMP.Record())
3✔
2063
        }
3✔
2064

2065
        if h.Metadata != nil {
3✔
UNCOV
2066
                records = append(records, record.NewMetadataRecord(&h.Metadata))
×
UNCOV
2067
        }
×
2068

2069
        if h.TotalAmtMsat != 0 {
6✔
2070
                totalMsatInt := uint64(h.TotalAmtMsat)
3✔
2071
                records = append(
3✔
2072
                        records, record.NewTotalAmtMsatBlinded(&totalMsatInt),
3✔
2073
                )
3✔
2074
        }
3✔
2075

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

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

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

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

2101
        for recordType, rawBytes := range mapRecords {
6✔
2102
                if err := WriteElements(w, recordType); err != nil {
3✔
2103
                        return err
×
2104
                }
×
2105

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

2111
        return nil
3✔
2112
}
2113

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

2118
func deserializeHop(r io.Reader) (*route.Hop, error) {
3✔
2119
        h := &route.Hop{}
3✔
2120

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

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

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

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

2145
        // If there're no elements, then we can return early.
2146
        if numElements == 0 {
6✔
2147
                return h, nil
3✔
2148
        }
3✔
2149

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

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

2164
                tlvMap[tlvType] = rawRecordBytes
3✔
2165
        }
2166

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

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

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

2198
        blindingType := uint64(record.BlindingPointOnionType)
3✔
2199
        if blindingPoint, ok := tlvMap[blindingType]; ok {
6✔
2200
                delete(tlvMap, blindingType)
3✔
2201

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

2209
        ampType := uint64(record.AMPOnionType)
3✔
2210
        if ampBytes, ok := tlvMap[ampType]; ok {
6✔
2211
                delete(tlvMap, ampType)
3✔
2212

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

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

×
UNCOV
2231
                h.Metadata = metadata
×
UNCOV
2232
        }
×
2233

2234
        totalAmtMsatType := uint64(record.TotalAmtMsatBlindedType)
3✔
2235
        if totalAmtMsat, ok := tlvMap[totalAmtMsatType]; ok {
6✔
2236
                delete(tlvMap, totalAmtMsatType)
3✔
2237

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

2251
                h.TotalAmtMsat = lnwire.MilliSatoshi(totalAmtMsatInt)
3✔
2252
        }
2253

2254
        h.CustomRecords = tlvMap
3✔
2255

3✔
2256
        return h, nil
3✔
2257
}
2258

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

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

2271
        for _, h := range r.Hops {
6✔
2272
                if err := serializeHop(w, h); err != nil {
3✔
2273
                        return err
×
2274
                }
×
2275
        }
2276

2277
        // Any new/extra TLV data is encoded in serializeHTLCAttemptInfo!
2278

2279
        return nil
3✔
2280
}
2281

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

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

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

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

3✔
2312
        // Any new/extra TLV data is decoded in deserializeHTLCAttemptInfo!
3✔
2313

3✔
2314
        return rt, nil
3✔
2315
}
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