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

lightningnetwork / lnd / 13586005509

28 Feb 2025 10:14AM UTC coverage: 68.629% (+9.9%) from 58.77%
13586005509

Pull #9521

github

web-flow
Merge 37d3a70a5 into 8532955b3
Pull Request #9521: unit: remove GOACC, use Go 1.20 native coverage functionality

129950 of 189351 relevant lines covered (68.63%)

23726.46 hits per line

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

69.51
/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.
63
func (db *DB) addPayment(payment *outgoingPayment) error {
5✔
64
        // Validate the field of the inner voice within the outgoing payment,
5✔
65
        // these must also adhere to the same constraints as regular invoices.
5✔
66
        if err := validateInvoice(&payment.Invoice); err != nil {
5✔
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.
73
        var b bytes.Buffer
5✔
74
        if err := serializeOutgoingPayment(&b, payment); err != nil {
5✔
75
                return err
×
76
        }
×
77
        paymentBytes := b.Bytes()
5✔
78

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

85
                // Obtain the new unique sequence number for this payment.
86
                paymentID, err := payments.NextSequence()
5✔
87
                if err != nil {
5✔
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.
94
                paymentIDBytes := make([]byte, 8)
5✔
95
                binary.BigEndian.PutUint64(paymentIDBytes, paymentID)
5✔
96

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

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

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

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

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

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

136
        return payments, nil
2✔
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.
143
func (db *DB) fetchPaymentStatus(paymentHash [32]byte) (PaymentStatus, error) {
4✔
144
        var paymentStatus = StatusUnknown
4✔
145
        err := kvdb.View(db, func(tx kvdb.RTx) error {
8✔
146
                var err error
4✔
147
                paymentStatus, err = fetchPaymentStatusTx(tx, paymentHash)
4✔
148
                return err
4✔
149
        }, func() {
8✔
150
                paymentStatus = StatusUnknown
4✔
151
        })
4✔
152
        if err != nil {
4✔
153
                return StatusUnknown, err
×
154
        }
×
155

156
        return paymentStatus, nil
4✔
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.
165
func fetchPaymentStatusTx(tx kvdb.RTx, paymentHash [32]byte) (PaymentStatus, error) {
4✔
166
        // The default status for all payments that aren't recorded in database.
4✔
167
        var paymentStatus = StatusUnknown
4✔
168

4✔
169
        bucket := tx.ReadBucket(paymentStatusBucket)
4✔
170
        if bucket == nil {
5✔
171
                return paymentStatus, nil
1✔
172
        }
1✔
173

174
        paymentStatusBytes := bucket.Get(paymentHash[:])
3✔
175
        if paymentStatusBytes == nil {
4✔
176
                return paymentStatus, nil
1✔
177
        }
1✔
178

179
        paymentStatus.FromBytes(paymentStatusBytes)
2✔
180

2✔
181
        return paymentStatus, nil
2✔
182
}
183

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

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

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

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

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

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

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

220
        return nil
5✔
221
}
222

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

10✔
226
        p := &outgoingPayment{}
10✔
227

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

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

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

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

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

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

261
        return p, nil
10✔
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.
268
func serializePaymentAttemptInfoMigration9(w io.Writer, a *PaymentAttemptInfo) error {
4✔
269
        if err := WriteElements(w, a.PaymentID, a.SessionKey); err != nil {
4✔
270
                return err
×
271
        }
×
272

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

277
        return nil
4✔
278
}
279

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

288
        return nil
19✔
289
}
290

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

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

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

308
        return nil
4✔
309
}
310

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

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

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

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

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

4✔
353
        return rt, nil
4✔
354
}
355

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

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

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

371
        return h, nil
19✔
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.
379
func (db *DB) fetchPaymentsMigration9() ([]*Payment, error) {
1✔
380
        var payments []*Payment
1✔
381

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

388
                return paymentsBucket.ForEach(func(k, v []byte) error {
4✔
389
                        bucket := paymentsBucket.NestedReadBucket(k)
3✔
390
                        if bucket == nil {
3✔
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

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

402
                        payments = append(payments, p)
3✔
403

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

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

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

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

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

443
        return payments, nil
1✔
444
}
445

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

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

457
        p.sequenceNum = binary.BigEndian.Uint64(seqBytes)
4✔
458

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

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

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

×
473
        }
×
474

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

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

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

501
        return p, nil
4✔
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