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

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

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

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

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

10
        lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
11
        "github.com/lightningnetwork/lnd/kvdb"
12
        "github.com/lightningnetwork/lnd/lntypes"
13
)
14

15
var (
16
        // paymentBucket is the name of the bucket within the database that
17
        // stores all data related to payments.
18
        //
19
        // Within the payments bucket, each invoice is keyed by its invoice ID
20
        // which is a monotonically increasing uint64.  BoltDB's sequence
21
        // feature is used for generating monotonically increasing id.
22
        //
23
        // NOTE: Deprecated. Kept around for migration purposes.
24
        paymentBucket = []byte("payments")
25

26
        // paymentStatusBucket is the name of the bucket within the database
27
        // that stores the status of a payment indexed by the payment's
28
        // preimage.
29
        //
30
        // NOTE: Deprecated. Kept around for migration purposes.
31
        paymentStatusBucket = []byte("payment-status")
32
)
33

34
// outgoingPayment represents a successful payment between the daemon and a
35
// remote node. Details such as the total fee paid, and the time of the payment
36
// are stored.
37
//
38
// NOTE: Deprecated. Kept around for migration purposes.
39
type outgoingPayment struct {
40
        Invoice
41

42
        // Fee is the total fee paid for the payment in milli-satoshis.
43
        Fee lnwire.MilliSatoshi
44

45
        // TotalTimeLock is the total cumulative time-lock in the HTLC extended
46
        // from the second-to-last hop to the destination.
47
        TimeLockLength uint32
48

49
        // Path encodes the path the payment took through the network. The path
50
        // excludes the outgoing node and consists of the hex-encoded
51
        // compressed public key of each of the nodes involved in the payment.
52
        Path [][33]byte
53

54
        // PaymentPreimage is the preImage of a successful payment. This is used
55
        // to calculate the PaymentHash as well as serve as a proof of payment.
56
        PaymentPreimage [32]byte
57
}
58

59
// addPayment saves a successful payment to the database. It is assumed that
60
// all payment are sent using unique payment hashes.
61
//
62
// NOTE: Deprecated. Kept around for migration purposes.
UNCOV
63
func (db *DB) addPayment(payment *outgoingPayment) error {
×
UNCOV
64
        // Validate the field of the inner voice within the outgoing payment,
×
UNCOV
65
        // these must also adhere to the same constraints as regular invoices.
×
UNCOV
66
        if err := validateInvoice(&payment.Invoice); err != nil {
×
67
                return err
×
68
        }
×
69

70
        // We first serialize the payment before starting the database
71
        // transaction so we can avoid creating a DB payment in the case of a
72
        // serialization error.
UNCOV
73
        var b bytes.Buffer
×
UNCOV
74
        if err := serializeOutgoingPayment(&b, payment); err != nil {
×
75
                return err
×
76
        }
×
UNCOV
77
        paymentBytes := b.Bytes()
×
UNCOV
78

×
UNCOV
79
        return kvdb.Update(db, func(tx kvdb.RwTx) error {
×
UNCOV
80
                payments, err := tx.CreateTopLevelBucket(paymentBucket)
×
UNCOV
81
                if err != nil {
×
82
                        return err
×
83
                }
×
84

85
                // Obtain the new unique sequence number for this payment.
UNCOV
86
                paymentID, err := payments.NextSequence()
×
UNCOV
87
                if err != nil {
×
88
                        return err
×
89
                }
×
90

91
                // We use BigEndian for keys as it orders keys in
92
                // ascending order. This allows bucket scans to order payments
93
                // in the order in which they were created.
UNCOV
94
                paymentIDBytes := make([]byte, 8)
×
UNCOV
95
                binary.BigEndian.PutUint64(paymentIDBytes, paymentID)
×
UNCOV
96

×
UNCOV
97
                return payments.Put(paymentIDBytes, paymentBytes)
×
UNCOV
98
        }, func() {})
×
99
}
100

101
// fetchAllPayments returns all outgoing payments in DB.
102
//
103
// NOTE: Deprecated. Kept around for migration purposes.
UNCOV
104
func (db *DB) fetchAllPayments() ([]*outgoingPayment, error) {
×
UNCOV
105
        var payments []*outgoingPayment
×
UNCOV
106

×
UNCOV
107
        err := kvdb.View(db, func(tx kvdb.RTx) error {
×
UNCOV
108
                bucket := tx.ReadBucket(paymentBucket)
×
UNCOV
109
                if bucket == nil {
×
110
                        return ErrNoPaymentsCreated
×
111
                }
×
112

UNCOV
113
                return bucket.ForEach(func(k, v []byte) error {
×
UNCOV
114
                        // If the value is nil, then we ignore it as it may be
×
UNCOV
115
                        // a sub-bucket.
×
UNCOV
116
                        if v == nil {
×
117
                                return nil
×
118
                        }
×
119

UNCOV
120
                        r := bytes.NewReader(v)
×
UNCOV
121
                        payment, err := deserializeOutgoingPayment(r)
×
UNCOV
122
                        if err != nil {
×
123
                                return err
×
124
                        }
×
125

UNCOV
126
                        payments = append(payments, payment)
×
UNCOV
127
                        return nil
×
128
                })
UNCOV
129
        }, func() {
×
UNCOV
130
                payments = nil
×
UNCOV
131
        })
×
UNCOV
132
        if err != nil {
×
133
                return nil, err
×
134
        }
×
135

UNCOV
136
        return payments, nil
×
137
}
138

139
// fetchPaymentStatus returns the payment status for outgoing payment.
140
// If status of the payment isn't found, it will default to "StatusUnknown".
141
//
142
// NOTE: Deprecated. Kept around for migration purposes.
UNCOV
143
func (db *DB) fetchPaymentStatus(paymentHash [32]byte) (PaymentStatus, error) {
×
UNCOV
144
        var paymentStatus = StatusUnknown
×
UNCOV
145
        err := kvdb.View(db, func(tx kvdb.RTx) error {
×
UNCOV
146
                var err error
×
UNCOV
147
                paymentStatus, err = fetchPaymentStatusTx(tx, paymentHash)
×
UNCOV
148
                return err
×
UNCOV
149
        }, func() {
×
UNCOV
150
                paymentStatus = StatusUnknown
×
UNCOV
151
        })
×
UNCOV
152
        if err != nil {
×
153
                return StatusUnknown, err
×
154
        }
×
155

UNCOV
156
        return paymentStatus, nil
×
157
}
158

159
// fetchPaymentStatusTx is a helper method that returns the payment status for
160
// outgoing payment.  If status of the payment isn't found, it will default to
161
// "StatusUnknown". It accepts the boltdb transactions such that this method
162
// can be composed into other atomic operations.
163
//
164
// NOTE: Deprecated. Kept around for migration purposes.
UNCOV
165
func fetchPaymentStatusTx(tx kvdb.RTx, paymentHash [32]byte) (PaymentStatus, error) {
×
UNCOV
166
        // The default status for all payments that aren't recorded in database.
×
UNCOV
167
        var paymentStatus = StatusUnknown
×
UNCOV
168

×
UNCOV
169
        bucket := tx.ReadBucket(paymentStatusBucket)
×
UNCOV
170
        if bucket == nil {
×
UNCOV
171
                return paymentStatus, nil
×
UNCOV
172
        }
×
173

UNCOV
174
        paymentStatusBytes := bucket.Get(paymentHash[:])
×
UNCOV
175
        if paymentStatusBytes == nil {
×
UNCOV
176
                return paymentStatus, nil
×
UNCOV
177
        }
×
178

UNCOV
179
        paymentStatus.FromBytes(paymentStatusBytes)
×
UNCOV
180

×
UNCOV
181
        return paymentStatus, nil
×
182
}
183

UNCOV
184
func serializeOutgoingPayment(w io.Writer, p *outgoingPayment) error {
×
UNCOV
185
        var scratch [8]byte
×
UNCOV
186

×
UNCOV
187
        if err := serializeInvoiceLegacy(w, &p.Invoice); err != nil {
×
188
                return err
×
189
        }
×
190

UNCOV
191
        byteOrder.PutUint64(scratch[:], uint64(p.Fee))
×
UNCOV
192
        if _, err := w.Write(scratch[:]); err != nil {
×
193
                return err
×
194
        }
×
195

196
        // First write out the length of the bytes to prefix the value.
UNCOV
197
        pathLen := uint32(len(p.Path))
×
UNCOV
198
        byteOrder.PutUint32(scratch[:4], pathLen)
×
UNCOV
199
        if _, err := w.Write(scratch[:4]); err != nil {
×
200
                return err
×
201
        }
×
202

203
        // Then with the path written, we write out the series of public keys
204
        // involved in the path.
UNCOV
205
        for _, hop := range p.Path {
×
UNCOV
206
                if _, err := w.Write(hop[:]); err != nil {
×
207
                        return err
×
208
                }
×
209
        }
210

UNCOV
211
        byteOrder.PutUint32(scratch[:4], p.TimeLockLength)
×
UNCOV
212
        if _, err := w.Write(scratch[:4]); err != nil {
×
213
                return err
×
214
        }
×
215

UNCOV
216
        if _, err := w.Write(p.PaymentPreimage[:]); err != nil {
×
217
                return err
×
218
        }
×
219

UNCOV
220
        return nil
×
221
}
222

UNCOV
223
func deserializeOutgoingPayment(r io.Reader) (*outgoingPayment, error) {
×
UNCOV
224
        var scratch [8]byte
×
UNCOV
225

×
UNCOV
226
        p := &outgoingPayment{}
×
UNCOV
227

×
UNCOV
228
        inv, err := deserializeInvoiceLegacy(r)
×
UNCOV
229
        if err != nil {
×
230
                return nil, err
×
231
        }
×
UNCOV
232
        p.Invoice = inv
×
UNCOV
233

×
UNCOV
234
        if _, err := r.Read(scratch[:]); err != nil {
×
235
                return nil, err
×
236
        }
×
UNCOV
237
        p.Fee = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
×
UNCOV
238

×
UNCOV
239
        if _, err = r.Read(scratch[:4]); err != nil {
×
240
                return nil, err
×
241
        }
×
UNCOV
242
        pathLen := byteOrder.Uint32(scratch[:4])
×
UNCOV
243

×
UNCOV
244
        path := make([][33]byte, pathLen)
×
UNCOV
245
        for i := uint32(0); i < pathLen; i++ {
×
UNCOV
246
                if _, err := r.Read(path[i][:]); err != nil {
×
247
                        return nil, err
×
248
                }
×
249
        }
UNCOV
250
        p.Path = path
×
UNCOV
251

×
UNCOV
252
        if _, err = r.Read(scratch[:4]); err != nil {
×
253
                return nil, err
×
254
        }
×
UNCOV
255
        p.TimeLockLength = byteOrder.Uint32(scratch[:4])
×
UNCOV
256

×
UNCOV
257
        if _, err := r.Read(p.PaymentPreimage[:]); err != nil {
×
258
                return nil, err
×
259
        }
×
260

UNCOV
261
        return p, nil
×
262
}
263

264
// serializePaymentAttemptInfoMigration9 is the serializePaymentAttemptInfo
265
// version as existed when migration #9 was created. We keep this around, along
266
// with the methods below to ensure that clients that upgrade will use the
267
// correct version of this method.
UNCOV
268
func serializePaymentAttemptInfoMigration9(w io.Writer, a *PaymentAttemptInfo) error {
×
UNCOV
269
        if err := WriteElements(w, a.PaymentID, a.SessionKey); err != nil {
×
270
                return err
×
271
        }
×
272

UNCOV
273
        if err := serializeRouteMigration9(w, a.Route); err != nil {
×
274
                return err
×
275
        }
×
276

UNCOV
277
        return nil
×
278
}
279

UNCOV
280
func serializeHopMigration9(w io.Writer, h *Hop) error {
×
UNCOV
281
        if err := WriteElements(w,
×
UNCOV
282
                h.PubKeyBytes[:], h.ChannelID, h.OutgoingTimeLock,
×
UNCOV
283
                h.AmtToForward,
×
UNCOV
284
        ); err != nil {
×
285
                return err
×
286
        }
×
287

UNCOV
288
        return nil
×
289
}
290

UNCOV
291
func serializeRouteMigration9(w io.Writer, r Route) error {
×
UNCOV
292
        if err := WriteElements(w,
×
UNCOV
293
                r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
×
UNCOV
294
        ); err != nil {
×
295
                return err
×
296
        }
×
297

UNCOV
298
        if err := WriteElements(w, uint32(len(r.Hops))); err != nil {
×
299
                return err
×
300
        }
×
301

UNCOV
302
        for _, h := range r.Hops {
×
UNCOV
303
                if err := serializeHopMigration9(w, h); err != nil {
×
304
                        return err
×
305
                }
×
306
        }
307

UNCOV
308
        return nil
×
309
}
310

UNCOV
311
func deserializePaymentAttemptInfoMigration9(r io.Reader) (*PaymentAttemptInfo, error) {
×
UNCOV
312
        a := &PaymentAttemptInfo{}
×
UNCOV
313
        err := ReadElements(r, &a.PaymentID, &a.SessionKey)
×
UNCOV
314
        if err != nil {
×
315
                return nil, err
×
316
        }
×
UNCOV
317
        a.Route, err = deserializeRouteMigration9(r)
×
UNCOV
318
        if err != nil {
×
319
                return nil, err
×
320
        }
×
UNCOV
321
        return a, nil
×
322
}
323

UNCOV
324
func deserializeRouteMigration9(r io.Reader) (Route, error) {
×
UNCOV
325
        rt := Route{}
×
UNCOV
326
        if err := ReadElements(r,
×
UNCOV
327
                &rt.TotalTimeLock, &rt.TotalAmount,
×
UNCOV
328
        ); err != nil {
×
329
                return rt, err
×
330
        }
×
331

UNCOV
332
        var pub []byte
×
UNCOV
333
        if err := ReadElements(r, &pub); err != nil {
×
334
                return rt, err
×
335
        }
×
UNCOV
336
        copy(rt.SourcePubKey[:], pub)
×
UNCOV
337

×
UNCOV
338
        var numHops uint32
×
UNCOV
339
        if err := ReadElements(r, &numHops); err != nil {
×
340
                return rt, err
×
341
        }
×
342

UNCOV
343
        var hops []*Hop
×
UNCOV
344
        for i := uint32(0); i < numHops; i++ {
×
UNCOV
345
                hop, err := deserializeHopMigration9(r)
×
UNCOV
346
                if err != nil {
×
347
                        return rt, err
×
348
                }
×
UNCOV
349
                hops = append(hops, hop)
×
350
        }
UNCOV
351
        rt.Hops = hops
×
UNCOV
352

×
UNCOV
353
        return rt, nil
×
354
}
355

UNCOV
356
func deserializeHopMigration9(r io.Reader) (*Hop, error) {
×
UNCOV
357
        h := &Hop{}
×
UNCOV
358

×
UNCOV
359
        var pub []byte
×
UNCOV
360
        if err := ReadElements(r, &pub); err != nil {
×
361
                return nil, err
×
362
        }
×
UNCOV
363
        copy(h.PubKeyBytes[:], pub)
×
UNCOV
364

×
UNCOV
365
        if err := ReadElements(r,
×
UNCOV
366
                &h.ChannelID, &h.OutgoingTimeLock, &h.AmtToForward,
×
UNCOV
367
        ); err != nil {
×
368
                return nil, err
×
369
        }
×
370

UNCOV
371
        return h, nil
×
372
}
373

374
// fetchPaymentsMigration9 returns all sent payments found in the DB using the
375
// payment attempt info format that was present as of migration #9. We need
376
// this as otherwise, the current FetchPayments version will use the latest
377
// decoding format. Note that we only need this for the
378
// TestOutgoingPaymentsMigration migration test case.
UNCOV
379
func (db *DB) fetchPaymentsMigration9() ([]*Payment, error) {
×
UNCOV
380
        var payments []*Payment
×
UNCOV
381

×
UNCOV
382
        err := kvdb.View(db, func(tx kvdb.RTx) error {
×
UNCOV
383
                paymentsBucket := tx.ReadBucket(paymentsRootBucket)
×
UNCOV
384
                if paymentsBucket == nil {
×
385
                        return nil
×
386
                }
×
387

UNCOV
388
                return paymentsBucket.ForEach(func(k, v []byte) error {
×
UNCOV
389
                        bucket := paymentsBucket.NestedReadBucket(k)
×
UNCOV
390
                        if bucket == nil {
×
391
                                // We only expect sub-buckets to be found in
×
392
                                // this top-level bucket.
×
393
                                return fmt.Errorf("non bucket element in " +
×
394
                                        "payments bucket")
×
395
                        }
×
396

UNCOV
397
                        p, err := fetchPaymentMigration9(bucket)
×
UNCOV
398
                        if err != nil {
×
399
                                return err
×
400
                        }
×
401

UNCOV
402
                        payments = append(payments, p)
×
UNCOV
403

×
UNCOV
404
                        // For older versions of lnd, duplicate payments to a
×
UNCOV
405
                        // payment has was possible. These will be found in a
×
UNCOV
406
                        // sub-bucket indexed by their sequence number if
×
UNCOV
407
                        // available.
×
UNCOV
408
                        dup := bucket.NestedReadBucket(paymentDuplicateBucket)
×
UNCOV
409
                        if dup == nil {
×
UNCOV
410
                                return nil
×
UNCOV
411
                        }
×
412

UNCOV
413
                        return dup.ForEach(func(k, v []byte) error {
×
UNCOV
414
                                subBucket := dup.NestedReadBucket(k)
×
UNCOV
415
                                if subBucket == nil {
×
416
                                        // We one bucket for each duplicate to
×
417
                                        // be found.
×
418
                                        return fmt.Errorf("non bucket element" +
×
419
                                                "in duplicate bucket")
×
420
                                }
×
421

UNCOV
422
                                p, err := fetchPaymentMigration9(subBucket)
×
UNCOV
423
                                if err != nil {
×
424
                                        return err
×
425
                                }
×
426

UNCOV
427
                                payments = append(payments, p)
×
UNCOV
428
                                return nil
×
429
                        })
430
                })
UNCOV
431
        }, func() {
×
UNCOV
432
                payments = nil
×
UNCOV
433
        })
×
UNCOV
434
        if err != nil {
×
435
                return nil, err
×
436
        }
×
437

438
        // Before returning, sort the payments by their sequence number.
UNCOV
439
        sort.Slice(payments, func(i, j int) bool {
×
UNCOV
440
                return payments[i].sequenceNum < payments[j].sequenceNum
×
UNCOV
441
        })
×
442

UNCOV
443
        return payments, nil
×
444
}
445

UNCOV
446
func fetchPaymentMigration9(bucket kvdb.RBucket) (*Payment, error) {
×
UNCOV
447
        var (
×
UNCOV
448
                err error
×
UNCOV
449
                p   = &Payment{}
×
UNCOV
450
        )
×
UNCOV
451

×
UNCOV
452
        seqBytes := bucket.Get(paymentSequenceKey)
×
UNCOV
453
        if seqBytes == nil {
×
454
                return nil, fmt.Errorf("sequence number not found")
×
455
        }
×
456

UNCOV
457
        p.sequenceNum = binary.BigEndian.Uint64(seqBytes)
×
UNCOV
458

×
UNCOV
459
        // Get the payment status.
×
UNCOV
460
        p.Status = fetchPaymentStatus(bucket)
×
UNCOV
461

×
UNCOV
462
        // Get the PaymentCreationInfo.
×
UNCOV
463
        b := bucket.Get(paymentCreationInfoKey)
×
UNCOV
464
        if b == nil {
×
465
                return nil, fmt.Errorf("creation info not found")
×
466
        }
×
467

UNCOV
468
        r := bytes.NewReader(b)
×
UNCOV
469
        p.Info, err = deserializePaymentCreationInfo(r)
×
UNCOV
470
        if err != nil {
×
471
                return nil, err
×
472

×
473
        }
×
474

475
        // Get the PaymentAttemptInfo. This can be unset.
UNCOV
476
        b = bucket.Get(paymentAttemptInfoKey)
×
UNCOV
477
        if b != nil {
×
UNCOV
478
                r = bytes.NewReader(b)
×
UNCOV
479
                p.Attempt, err = deserializePaymentAttemptInfoMigration9(r)
×
UNCOV
480
                if err != nil {
×
481
                        return nil, err
×
482
                }
×
483
        }
484

485
        // Get the payment preimage. This is only found for
486
        // completed payments.
UNCOV
487
        b = bucket.Get(paymentSettleInfoKey)
×
UNCOV
488
        if b != nil {
×
UNCOV
489
                var preimg lntypes.Preimage
×
UNCOV
490
                copy(preimg[:], b[:])
×
UNCOV
491
                p.PaymentPreimage = &preimg
×
UNCOV
492
        }
×
493

494
        // Get failure reason if available.
UNCOV
495
        b = bucket.Get(paymentFailInfoKey)
×
UNCOV
496
        if b != nil {
×
497
                reason := FailureReason(b[0])
×
498
                p.Failure = &reason
×
499
        }
×
500

UNCOV
501
        return p, nil
×
502
}
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