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

lightningnetwork / lnd / 13035292482

29 Jan 2025 03:59PM UTC coverage: 49.3% (-9.5%) from 58.777%
13035292482

Pull #9456

github

mohamedawnallah
docs: update release-notes-0.19.0.md

In this commit, we warn users about the removal
of RPCs `SendToRoute`, `SendToRouteSync`, `SendPayment`,
and `SendPaymentSync` in the next release 0.20.
Pull Request #9456: lnrpc+docs: deprecate warning `SendToRoute`, `SendToRouteSync`, `SendPayment`, and `SendPaymentSync` in Release 0.19

100634 of 204126 relevant lines covered (49.3%)

1.54 hits per line

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

0.0
/channeldb/migration_01_to_11/payments.go
1
package migration_01_to_11
2

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

12
        "github.com/btcsuite/btcd/btcec/v2"
13
        "github.com/btcsuite/btcd/wire"
14
        lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
15
        "github.com/lightningnetwork/lnd/kvdb"
16
        "github.com/lightningnetwork/lnd/lntypes"
17
        "github.com/lightningnetwork/lnd/tlv"
18
)
19

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

57
        // paymentDublicateBucket is the name of a optional sub-bucket within
58
        // the payment hash bucket, that is used to hold duplicate payments to
59
        // a payment hash. This is needed to support information from earlier
60
        // versions of lnd, where it was possible to pay to a payment hash more
61
        // than once.
62
        paymentDuplicateBucket = []byte("payment-duplicate-bucket")
63

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

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

72
        // paymentAttemptInfoKey is a key used in the payment's sub-bucket to
73
        // store the info about the latest attempt that was done for the
74
        // payment in question.
75
        paymentAttemptInfoKey = []byte("payment-attempt-info")
76

77
        // paymentSettleInfoKey is a key used in the payment's sub-bucket to
78
        // store the settle info of the payment.
79
        paymentSettleInfoKey = []byte("payment-settle-info")
80

81
        // paymentFailInfoKey is a key used in the payment's sub-bucket to
82
        // store information about the reason a payment failed.
83
        paymentFailInfoKey = []byte("payment-fail-info")
84
)
85

86
// FailureReason encodes the reason a payment ultimately failed.
87
type FailureReason byte
88

89
const (
90
        // FailureReasonTimeout indicates that the payment did timeout before a
91
        // successful payment attempt was made.
92
        FailureReasonTimeout FailureReason = 0
93

94
        // FailureReasonNoRoute indicates no successful route to the
95
        // destination was found during path finding.
96
        FailureReasonNoRoute FailureReason = 1
97

98
        // FailureReasonError indicates that an unexpected error happened during
99
        // payment.
100
        FailureReasonError FailureReason = 2
101

102
        // FailureReasonIncorrectPaymentDetails indicates that either the hash
103
        // is unknown or the final cltv delta or amount is incorrect.
104
        FailureReasonIncorrectPaymentDetails FailureReason = 3
105

106
        // TODO(halseth): cancel state.
107

108
        // TODO(joostjager): Add failure reasons for:
109
        // LocalLiquidityInsufficient, RemoteCapacityInsufficient.
110
)
111

112
// String returns a human readable FailureReason
113
func (r FailureReason) String() string {
×
114
        switch r {
×
115
        case FailureReasonTimeout:
×
116
                return "timeout"
×
117
        case FailureReasonNoRoute:
×
118
                return "no_route"
×
119
        case FailureReasonError:
×
120
                return "error"
×
121
        case FailureReasonIncorrectPaymentDetails:
×
122
                return "incorrect_payment_details"
×
123
        }
124

125
        return "unknown"
×
126
}
127

128
// PaymentStatus represent current status of payment
129
type PaymentStatus byte
130

131
const (
132
        // StatusUnknown is the status where a payment has never been initiated
133
        // and hence is unknown.
134
        StatusUnknown PaymentStatus = 0
135

136
        // StatusInFlight is the status where a payment has been initiated, but
137
        // a response has not been received.
138
        StatusInFlight PaymentStatus = 1
139

140
        // StatusSucceeded is the status where a payment has been initiated and
141
        // the payment was completed successfully.
142
        StatusSucceeded PaymentStatus = 2
143

144
        // StatusFailed is the status where a payment has been initiated and a
145
        // failure result has come back.
146
        StatusFailed PaymentStatus = 3
147
)
148

149
// Bytes returns status as slice of bytes.
150
func (ps PaymentStatus) Bytes() []byte {
×
151
        return []byte{byte(ps)}
×
152
}
×
153

154
// FromBytes sets status from slice of bytes.
155
func (ps *PaymentStatus) FromBytes(status []byte) error {
×
156
        if len(status) != 1 {
×
157
                return errors.New("payment status is empty")
×
158
        }
×
159

160
        switch PaymentStatus(status[0]) {
×
161
        case StatusUnknown, StatusInFlight, StatusSucceeded, StatusFailed:
×
162
                *ps = PaymentStatus(status[0])
×
163
        default:
×
164
                return errors.New("unknown payment status")
×
165
        }
166

167
        return nil
×
168
}
169

170
// String returns readable representation of payment status.
171
func (ps PaymentStatus) String() string {
×
172
        switch ps {
×
173
        case StatusUnknown:
×
174
                return "Unknown"
×
175
        case StatusInFlight:
×
176
                return "In Flight"
×
177
        case StatusSucceeded:
×
178
                return "Succeeded"
×
179
        case StatusFailed:
×
180
                return "Failed"
×
181
        default:
×
182
                return "Unknown"
×
183
        }
184
}
185

186
// PaymentCreationInfo is the information necessary to have ready when
187
// initiating a payment, moving it into state InFlight.
188
type PaymentCreationInfo struct {
189
        // PaymentHash is the hash this payment is paying to.
190
        PaymentHash lntypes.Hash
191

192
        // Value is the amount we are paying.
193
        Value lnwire.MilliSatoshi
194

195
        // CreatingDate is the time when this payment was initiated.
196
        CreationDate time.Time
197

198
        // PaymentRequest is the full payment request, if any.
199
        PaymentRequest []byte
200
}
201

202
// PaymentAttemptInfo contains information about a specific payment attempt for
203
// a given payment. This information is used by the router to handle any errors
204
// coming back after an attempt is made, and to query the switch about the
205
// status of a payment. For settled payment this will be the information for
206
// the succeeding payment attempt.
207
type PaymentAttemptInfo struct {
208
        // PaymentID is the unique ID used for this attempt.
209
        PaymentID uint64
210

211
        // SessionKey is the ephemeral key used for this payment attempt.
212
        SessionKey *btcec.PrivateKey
213

214
        // Route is the route attempted to send the HTLC.
215
        Route Route
216
}
217

218
// Payment is a wrapper around a payment's PaymentCreationInfo,
219
// PaymentAttemptInfo, and preimage. All payments will have the
220
// PaymentCreationInfo set, the PaymentAttemptInfo will be set only if at least
221
// one payment attempt has been made, while only completed payments will have a
222
// non-zero payment preimage.
223
type Payment struct {
224
        // sequenceNum is a unique identifier used to sort the payments in
225
        // order of creation.
226
        sequenceNum uint64
227

228
        // Status is the current PaymentStatus of this payment.
229
        Status PaymentStatus
230

231
        // Info holds all static information about this payment, and is
232
        // populated when the payment is initiated.
233
        Info *PaymentCreationInfo
234

235
        // Attempt is the information about the last payment attempt made.
236
        //
237
        // NOTE: Can be nil if no attempt is yet made.
238
        Attempt *PaymentAttemptInfo
239

240
        // PaymentPreimage is the preimage of a successful payment. This serves
241
        // as a proof of payment. It will only be non-nil for settled payments.
242
        //
243
        // NOTE: Can be nil if payment is not settled.
244
        PaymentPreimage *lntypes.Preimage
245

246
        // Failure is a failure reason code indicating the reason the payment
247
        // failed. It is only non-nil for failed payments.
248
        //
249
        // NOTE: Can be nil if payment is not failed.
250
        Failure *FailureReason
251
}
252

253
// FetchPayments returns all sent payments found in the DB.
254
func (db *DB) FetchPayments() ([]*Payment, error) {
×
255
        var payments []*Payment
×
256

×
257
        err := kvdb.View(db, func(tx kvdb.RTx) error {
×
258
                paymentsBucket := tx.ReadBucket(paymentsRootBucket)
×
259
                if paymentsBucket == nil {
×
260
                        return nil
×
261
                }
×
262

263
                return paymentsBucket.ForEach(func(k, v []byte) error {
×
264
                        bucket := paymentsBucket.NestedReadBucket(k)
×
265
                        if bucket == nil {
×
266
                                // We only expect sub-buckets to be found in
×
267
                                // this top-level bucket.
×
268
                                return fmt.Errorf("non bucket element in " +
×
269
                                        "payments bucket")
×
270
                        }
×
271

272
                        p, err := fetchPayment(bucket)
×
273
                        if err != nil {
×
274
                                return err
×
275
                        }
×
276

277
                        payments = append(payments, p)
×
278

×
279
                        // For older versions of lnd, duplicate payments to a
×
280
                        // payment has was possible. These will be found in a
×
281
                        // sub-bucket indexed by their sequence number if
×
282
                        // available.
×
283
                        dup := bucket.NestedReadBucket(paymentDuplicateBucket)
×
284
                        if dup == nil {
×
285
                                return nil
×
286
                        }
×
287

288
                        return dup.ForEach(func(k, v []byte) error {
×
289
                                subBucket := dup.NestedReadBucket(k)
×
290
                                if subBucket == nil {
×
291
                                        // We one bucket for each duplicate to
×
292
                                        // be found.
×
293
                                        return fmt.Errorf("non bucket element" +
×
294
                                                "in duplicate bucket")
×
295
                                }
×
296

297
                                p, err := fetchPayment(subBucket)
×
298
                                if err != nil {
×
299
                                        return err
×
300
                                }
×
301

302
                                payments = append(payments, p)
×
303
                                return nil
×
304
                        })
305
                })
306
        }, func() {
×
307
                payments = nil
×
308
        })
×
309
        if err != nil {
×
310
                return nil, err
×
311
        }
×
312

313
        // Before returning, sort the payments by their sequence number.
314
        sort.Slice(payments, func(i, j int) bool {
×
315
                return payments[i].sequenceNum < payments[j].sequenceNum
×
316
        })
×
317

318
        return payments, nil
×
319
}
320

321
func fetchPayment(bucket kvdb.RBucket) (*Payment, error) {
×
322
        var (
×
323
                err error
×
324
                p   = &Payment{}
×
325
        )
×
326

×
327
        seqBytes := bucket.Get(paymentSequenceKey)
×
328
        if seqBytes == nil {
×
329
                return nil, fmt.Errorf("sequence number not found")
×
330
        }
×
331

332
        p.sequenceNum = binary.BigEndian.Uint64(seqBytes)
×
333

×
334
        // Get the payment status.
×
335
        p.Status = fetchPaymentStatus(bucket)
×
336

×
337
        // Get the PaymentCreationInfo.
×
338
        b := bucket.Get(paymentCreationInfoKey)
×
339
        if b == nil {
×
340
                return nil, fmt.Errorf("creation info not found")
×
341
        }
×
342

343
        r := bytes.NewReader(b)
×
344
        p.Info, err = deserializePaymentCreationInfo(r)
×
345
        if err != nil {
×
346
                return nil, err
×
347

×
348
        }
×
349

350
        // Get the PaymentAttemptInfo. This can be unset.
351
        b = bucket.Get(paymentAttemptInfoKey)
×
352
        if b != nil {
×
353
                r = bytes.NewReader(b)
×
354
                p.Attempt, err = deserializePaymentAttemptInfo(r)
×
355
                if err != nil {
×
356
                        return nil, err
×
357
                }
×
358
        }
359

360
        // Get the payment preimage. This is only found for
361
        // completed payments.
362
        b = bucket.Get(paymentSettleInfoKey)
×
363
        if b != nil {
×
364
                var preimg lntypes.Preimage
×
365
                copy(preimg[:], b[:])
×
366
                p.PaymentPreimage = &preimg
×
367
        }
×
368

369
        // Get failure reason if available.
370
        b = bucket.Get(paymentFailInfoKey)
×
371
        if b != nil {
×
372
                reason := FailureReason(b[0])
×
373
                p.Failure = &reason
×
374
        }
×
375

376
        return p, nil
×
377
}
378

379
func serializePaymentCreationInfo(w io.Writer, c *PaymentCreationInfo) error {
×
380
        var scratch [8]byte
×
381

×
382
        if _, err := w.Write(c.PaymentHash[:]); err != nil {
×
383
                return err
×
384
        }
×
385

386
        byteOrder.PutUint64(scratch[:], uint64(c.Value))
×
387
        if _, err := w.Write(scratch[:]); err != nil {
×
388
                return err
×
389
        }
×
390

391
        byteOrder.PutUint64(scratch[:], uint64(c.CreationDate.Unix()))
×
392
        if _, err := w.Write(scratch[:]); err != nil {
×
393
                return err
×
394
        }
×
395

396
        byteOrder.PutUint32(scratch[:4], uint32(len(c.PaymentRequest)))
×
397
        if _, err := w.Write(scratch[:4]); err != nil {
×
398
                return err
×
399
        }
×
400

401
        if _, err := w.Write(c.PaymentRequest[:]); err != nil {
×
402
                return err
×
403
        }
×
404

405
        return nil
×
406
}
407

408
func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo, error) {
×
409
        var scratch [8]byte
×
410

×
411
        c := &PaymentCreationInfo{}
×
412

×
413
        if _, err := io.ReadFull(r, c.PaymentHash[:]); err != nil {
×
414
                return nil, err
×
415
        }
×
416

417
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
×
418
                return nil, err
×
419
        }
×
420
        c.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
×
421

×
422
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
×
423
                return nil, err
×
424
        }
×
425
        c.CreationDate = time.Unix(int64(byteOrder.Uint64(scratch[:])), 0)
×
426

×
427
        if _, err := io.ReadFull(r, scratch[:4]); err != nil {
×
428
                return nil, err
×
429
        }
×
430

431
        reqLen := uint32(byteOrder.Uint32(scratch[:4]))
×
432
        payReq := make([]byte, reqLen)
×
433
        if reqLen > 0 {
×
434
                if _, err := io.ReadFull(r, payReq[:]); err != nil {
×
435
                        return nil, err
×
436
                }
×
437
        }
438
        c.PaymentRequest = payReq
×
439

×
440
        return c, nil
×
441
}
442

443
func serializePaymentAttemptInfo(w io.Writer, a *PaymentAttemptInfo) error {
×
444
        if err := WriteElements(w, a.PaymentID, a.SessionKey); err != nil {
×
445
                return err
×
446
        }
×
447

448
        if err := SerializeRoute(w, a.Route); err != nil {
×
449
                return err
×
450
        }
×
451

452
        return nil
×
453
}
454

455
func deserializePaymentAttemptInfo(r io.Reader) (*PaymentAttemptInfo, error) {
×
456
        a := &PaymentAttemptInfo{}
×
457
        err := ReadElements(r, &a.PaymentID, &a.SessionKey)
×
458
        if err != nil {
×
459
                return nil, err
×
460
        }
×
461
        a.Route, err = DeserializeRoute(r)
×
462
        if err != nil {
×
463
                return nil, err
×
464
        }
×
465
        return a, nil
×
466
}
467

468
func serializeHop(w io.Writer, h *Hop) error {
×
469
        if err := WriteElements(w,
×
470
                h.PubKeyBytes[:], h.ChannelID, h.OutgoingTimeLock,
×
471
                h.AmtToForward,
×
472
        ); err != nil {
×
473
                return err
×
474
        }
×
475

476
        if err := binary.Write(w, byteOrder, h.LegacyPayload); err != nil {
×
477
                return err
×
478
        }
×
479

480
        // For legacy payloads, we don't need to write any TLV records, so
481
        // we'll write a zero indicating the our serialized TLV map has no
482
        // records.
483
        if h.LegacyPayload {
×
484
                return WriteElements(w, uint32(0))
×
485
        }
×
486

487
        // Otherwise, we'll transform our slice of records into a map of the
488
        // raw bytes, then serialize them in-line with a length (number of
489
        // elements) prefix.
490
        mapRecords, err := tlv.RecordsToMap(h.TLVRecords)
×
491
        if err != nil {
×
492
                return err
×
493
        }
×
494

495
        numRecords := uint32(len(mapRecords))
×
496
        if err := WriteElements(w, numRecords); err != nil {
×
497
                return err
×
498
        }
×
499

500
        for recordType, rawBytes := range mapRecords {
×
501
                if err := WriteElements(w, recordType); err != nil {
×
502
                        return err
×
503
                }
×
504

505
                if err := wire.WriteVarBytes(w, 0, rawBytes); err != nil {
×
506
                        return err
×
507
                }
×
508
        }
509

510
        return nil
×
511
}
512

513
// maxOnionPayloadSize is the largest Sphinx payload possible, so we don't need
514
// to read/write a TLV stream larger than this.
515
const maxOnionPayloadSize = 1300
516

517
func deserializeHop(r io.Reader) (*Hop, error) {
×
518
        h := &Hop{}
×
519

×
520
        var pub []byte
×
521
        if err := ReadElements(r, &pub); err != nil {
×
522
                return nil, err
×
523
        }
×
524
        copy(h.PubKeyBytes[:], pub)
×
525

×
526
        if err := ReadElements(r,
×
527
                &h.ChannelID, &h.OutgoingTimeLock, &h.AmtToForward,
×
528
        ); err != nil {
×
529
                return nil, err
×
530
        }
×
531

532
        // TODO(roasbeef): change field to allow LegacyPayload false to be the
533
        // legacy default?
534
        err := binary.Read(r, byteOrder, &h.LegacyPayload)
×
535
        if err != nil {
×
536
                return nil, err
×
537
        }
×
538

539
        var numElements uint32
×
540
        if err := ReadElements(r, &numElements); err != nil {
×
541
                return nil, err
×
542
        }
×
543

544
        // If there're no elements, then we can return early.
545
        if numElements == 0 {
×
546
                return h, nil
×
547
        }
×
548

549
        tlvMap := make(map[uint64][]byte)
×
550
        for i := uint32(0); i < numElements; i++ {
×
551
                var tlvType uint64
×
552
                if err := ReadElements(r, &tlvType); err != nil {
×
553
                        return nil, err
×
554
                }
×
555

556
                rawRecordBytes, err := wire.ReadVarBytes(
×
557
                        r, 0, maxOnionPayloadSize, "tlv",
×
558
                )
×
559
                if err != nil {
×
560
                        return nil, err
×
561
                }
×
562

563
                tlvMap[tlvType] = rawRecordBytes
×
564
        }
565

566
        h.TLVRecords = tlv.MapToRecords(tlvMap)
×
567

×
568
        return h, nil
×
569
}
570

571
// SerializeRoute serializes a route.
572
func SerializeRoute(w io.Writer, r Route) error {
×
573
        if err := WriteElements(w,
×
574
                r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
×
575
        ); err != nil {
×
576
                return err
×
577
        }
×
578

579
        if err := WriteElements(w, uint32(len(r.Hops))); err != nil {
×
580
                return err
×
581
        }
×
582

583
        for _, h := range r.Hops {
×
584
                if err := serializeHop(w, h); err != nil {
×
585
                        return err
×
586
                }
×
587
        }
588

589
        return nil
×
590
}
591

592
// DeserializeRoute deserializes a route.
593
func DeserializeRoute(r io.Reader) (Route, error) {
×
594
        rt := Route{}
×
595
        if err := ReadElements(r,
×
596
                &rt.TotalTimeLock, &rt.TotalAmount,
×
597
        ); err != nil {
×
598
                return rt, err
×
599
        }
×
600

601
        var pub []byte
×
602
        if err := ReadElements(r, &pub); err != nil {
×
603
                return rt, err
×
604
        }
×
605
        copy(rt.SourcePubKey[:], pub)
×
606

×
607
        var numHops uint32
×
608
        if err := ReadElements(r, &numHops); err != nil {
×
609
                return rt, err
×
610
        }
×
611

612
        var hops []*Hop
×
613
        for i := uint32(0); i < numHops; i++ {
×
614
                hop, err := deserializeHop(r)
×
615
                if err != nil {
×
616
                        return rt, err
×
617
                }
×
618
                hops = append(hops, hop)
×
619
        }
620
        rt.Hops = hops
×
621

×
622
        return rt, nil
×
623
}
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