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

lightningnetwork / lnd / 13566028875

27 Feb 2025 12:09PM UTC coverage: 49.396% (-9.4%) from 58.748%
13566028875

Pull #9555

github

ellemouton
graph/db: populate the graph cache in Start instead of during construction

In this commit, we move the graph cache population logic out of the
ChannelGraph constructor and into its Start method instead.
Pull Request #9555: graph: extract cache from CRUD [6]

34 of 54 new or added lines in 4 files covered. (62.96%)

27464 existing lines in 436 files now uncovered.

101095 of 204664 relevant lines covered (49.4%)

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/invoices.go
1
package migration_01_to_11
2

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

10
        "github.com/btcsuite/btcd/wire"
11
        lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
12
        "github.com/lightningnetwork/lnd/kvdb"
13
        "github.com/lightningnetwork/lnd/lntypes"
14
        "github.com/lightningnetwork/lnd/tlv"
15
)
16

17
var (
18

19
        // invoiceBucket is the name of the bucket within the database that
20
        // stores all data related to invoices no matter their final state.
21
        // Within the invoice bucket, each invoice is keyed by its invoice ID
22
        // which is a monotonically increasing uint32.
23
        invoiceBucket = []byte("invoices")
24

25
        // addIndexBucket is an index bucket that we'll use to create a
26
        // monotonically increasing set of add indexes. Each time we add a new
27
        // invoice, this sequence number will be incremented and then populated
28
        // within the new invoice.
29
        //
30
        // In addition to this sequence number, we map:
31
        //
32
        //   addIndexNo => invoiceKey
33
        addIndexBucket = []byte("invoice-add-index")
34

35
        // settleIndexBucket is an index bucket that we'll use to create a
36
        // monotonically increasing integer for tracking a "settle index". Each
37
        // time an invoice is settled, this sequence number will be incremented
38
        // as populate within the newly settled invoice.
39
        //
40
        // In addition to this sequence number, we map:
41
        //
42
        //   settleIndexNo => invoiceKey
43
        settleIndexBucket = []byte("invoice-settle-index")
44
)
45

46
const (
47
        // MaxMemoSize is maximum size of the memo field within invoices stored
48
        // in the database.
49
        MaxMemoSize = 1024
50

51
        // MaxReceiptSize is the maximum size of the payment receipt stored
52
        // within the database along side incoming/outgoing invoices.
53
        MaxReceiptSize = 1024
54

55
        // MaxPaymentRequestSize is the max size of a payment request for
56
        // this invoice.
57
        // TODO(halseth): determine the max length payment request when field
58
        // lengths are final.
59
        MaxPaymentRequestSize = 4096
60

61
        // A set of tlv type definitions used to serialize invoice htlcs to the
62
        // database.
63
        chanIDType       tlv.Type = 1
64
        htlcIDType       tlv.Type = 3
65
        amtType          tlv.Type = 5
66
        acceptHeightType tlv.Type = 7
67
        acceptTimeType   tlv.Type = 9
68
        resolveTimeType  tlv.Type = 11
69
        expiryHeightType tlv.Type = 13
70
        stateType        tlv.Type = 15
71
)
72

73
// ContractState describes the state the invoice is in.
74
type ContractState uint8
75

76
const (
77
        // ContractOpen means the invoice has only been created.
78
        ContractOpen ContractState = 0
79

80
        // ContractSettled means the htlc is settled and the invoice has been
81
        // paid.
82
        ContractSettled ContractState = 1
83

84
        // ContractCanceled means the invoice has been canceled.
85
        ContractCanceled ContractState = 2
86

87
        // ContractAccepted means the HTLC has been accepted but not settled
88
        // yet.
89
        ContractAccepted ContractState = 3
90
)
91

92
// String returns a human readable identifier for the ContractState type.
93
func (c ContractState) String() string {
×
94
        switch c {
×
95
        case ContractOpen:
×
96
                return "Open"
×
97
        case ContractSettled:
×
98
                return "Settled"
×
99
        case ContractCanceled:
×
100
                return "Canceled"
×
101
        case ContractAccepted:
×
102
                return "Accepted"
×
103
        }
104

105
        return "Unknown"
×
106
}
107

108
// ContractTerm is a companion struct to the Invoice struct. This struct houses
109
// the necessary conditions required before the invoice can be considered fully
110
// settled by the payee.
111
type ContractTerm struct {
112
        // PaymentPreimage is the preimage which is to be revealed in the
113
        // occasion that an HTLC paying to the hash of this preimage is
114
        // extended.
115
        PaymentPreimage lntypes.Preimage
116

117
        // Value is the expected amount of milli-satoshis to be paid to an HTLC
118
        // which can be satisfied by the above preimage.
119
        Value lnwire.MilliSatoshi
120

121
        // State describes the state the invoice is in.
122
        State ContractState
123
}
124

125
// Invoice is a payment invoice generated by a payee in order to request
126
// payment for some good or service. The inclusion of invoices within Lightning
127
// creates a payment work flow for merchants very similar to that of the
128
// existing financial system within PayPal, etc.  Invoices are added to the
129
// database when a payment is requested, then can be settled manually once the
130
// payment is received at the upper layer. For record keeping purposes,
131
// invoices are never deleted from the database, instead a bit is toggled
132
// denoting the invoice has been fully settled. Within the database, all
133
// invoices must have a unique payment hash which is generated by taking the
134
// sha256 of the payment preimage.
135
type Invoice struct {
136
        // Memo is an optional memo to be stored along side an invoice.  The
137
        // memo may contain further details pertaining to the invoice itself,
138
        // or any other message which fits within the size constraints.
139
        Memo []byte
140

141
        // Receipt is an optional field dedicated for storing a
142
        // cryptographically binding receipt of payment.
143
        //
144
        // TODO(roasbeef): document scheme.
145
        Receipt []byte
146

147
        // PaymentRequest is an optional field where a payment request created
148
        // for this invoice can be stored.
149
        PaymentRequest []byte
150

151
        // FinalCltvDelta is the minimum required number of blocks before htlc
152
        // expiry when the invoice is accepted.
153
        FinalCltvDelta int32
154

155
        // Expiry defines how long after creation this invoice should expire.
156
        Expiry time.Duration
157

158
        // CreationDate is the exact time the invoice was created.
159
        CreationDate time.Time
160

161
        // SettleDate is the exact time the invoice was settled.
162
        SettleDate time.Time
163

164
        // Terms are the contractual payment terms of the invoice. Once all the
165
        // terms have been satisfied by the payer, then the invoice can be
166
        // considered fully fulfilled.
167
        //
168
        // TODO(roasbeef): later allow for multiple terms to fulfill the final
169
        // invoice: payment fragmentation, etc.
170
        Terms ContractTerm
171

172
        // AddIndex is an auto-incrementing integer that acts as a
173
        // monotonically increasing sequence number for all invoices created.
174
        // Clients can then use this field as a "checkpoint" of sorts when
175
        // implementing a streaming RPC to notify consumers of instances where
176
        // an invoice has been added before they re-connected.
177
        //
178
        // NOTE: This index starts at 1.
179
        AddIndex uint64
180

181
        // SettleIndex is an auto-incrementing integer that acts as a
182
        // monotonically increasing sequence number for all settled invoices.
183
        // Clients can then use this field as a "checkpoint" of sorts when
184
        // implementing a streaming RPC to notify consumers of instances where
185
        // an invoice has been settled before they re-connected.
186
        //
187
        // NOTE: This index starts at 1.
188
        SettleIndex uint64
189

190
        // AmtPaid is the final amount that we ultimately accepted for pay for
191
        // this invoice. We specify this value independently as it's possible
192
        // that the invoice originally didn't specify an amount, or the sender
193
        // overpaid.
194
        AmtPaid lnwire.MilliSatoshi
195

196
        // Htlcs records all htlcs that paid to this invoice. Some of these
197
        // htlcs may have been marked as canceled.
198
        Htlcs map[CircuitKey]*InvoiceHTLC
199
}
200

201
// HtlcState defines the states an htlc paying to an invoice can be in.
202
type HtlcState uint8
203

204
// InvoiceHTLC contains details about an htlc paying to this invoice.
205
type InvoiceHTLC struct {
206
        // Amt is the amount that is carried by this htlc.
207
        Amt lnwire.MilliSatoshi
208

209
        // AcceptHeight is the block height at which the invoice registry
210
        // decided to accept this htlc as a payment to the invoice. At this
211
        // height, the invoice cltv delay must have been met.
212
        AcceptHeight uint32
213

214
        // AcceptTime is the wall clock time at which the invoice registry
215
        // decided to accept the htlc.
216
        AcceptTime time.Time
217

218
        // ResolveTime is the wall clock time at which the invoice registry
219
        // decided to settle the htlc.
220
        ResolveTime time.Time
221

222
        // Expiry is the expiry height of this htlc.
223
        Expiry uint32
224

225
        // State indicates the state the invoice htlc is currently in. A
226
        // canceled htlc isn't just removed from the invoice htlcs map, because
227
        // we need AcceptHeight to properly cancel the htlc back.
228
        State HtlcState
229
}
230

UNCOV
231
func validateInvoice(i *Invoice) error {
×
UNCOV
232
        if len(i.Memo) > MaxMemoSize {
×
233
                return fmt.Errorf("max length a memo is %v, and invoice "+
×
234
                        "of length %v was provided", MaxMemoSize, len(i.Memo))
×
235
        }
×
UNCOV
236
        if len(i.Receipt) > MaxReceiptSize {
×
237
                return fmt.Errorf("max length a receipt is %v, and invoice "+
×
238
                        "of length %v was provided", MaxReceiptSize,
×
239
                        len(i.Receipt))
×
240
        }
×
UNCOV
241
        if len(i.PaymentRequest) > MaxPaymentRequestSize {
×
242
                return fmt.Errorf("max length of payment request is %v, length "+
×
243
                        "provided was %v", MaxPaymentRequestSize,
×
244
                        len(i.PaymentRequest))
×
245
        }
×
UNCOV
246
        return nil
×
247
}
248

249
// FetchAllInvoices returns all invoices currently stored within the database.
250
// If the pendingOnly param is true, then only unsettled invoices will be
251
// returned, skipping all invoices that are fully settled.
UNCOV
252
func (d *DB) FetchAllInvoices(pendingOnly bool) ([]Invoice, error) {
×
UNCOV
253
        var invoices []Invoice
×
UNCOV
254

×
UNCOV
255
        err := kvdb.View(d, func(tx kvdb.RTx) error {
×
UNCOV
256
                invoiceB := tx.ReadBucket(invoiceBucket)
×
UNCOV
257
                if invoiceB == nil {
×
258
                        return ErrNoInvoicesCreated
×
259
                }
×
260

261
                // Iterate through the entire key space of the top-level
262
                // invoice bucket. If key with a non-nil value stores the next
263
                // invoice ID which maps to the corresponding invoice.
UNCOV
264
                return invoiceB.ForEach(func(k, v []byte) error {
×
UNCOV
265
                        if v == nil {
×
266
                                return nil
×
267
                        }
×
268

UNCOV
269
                        invoiceReader := bytes.NewReader(v)
×
UNCOV
270
                        invoice, err := deserializeInvoice(invoiceReader)
×
UNCOV
271
                        if err != nil {
×
272
                                return err
×
273
                        }
×
274

UNCOV
275
                        if pendingOnly &&
×
UNCOV
276
                                invoice.Terms.State == ContractSettled {
×
277

×
278
                                return nil
×
279
                        }
×
280

UNCOV
281
                        invoices = append(invoices, invoice)
×
UNCOV
282

×
UNCOV
283
                        return nil
×
284
                })
UNCOV
285
        }, func() {
×
UNCOV
286
                invoices = nil
×
UNCOV
287
        })
×
UNCOV
288
        if err != nil {
×
289
                return nil, err
×
290
        }
×
291

UNCOV
292
        return invoices, nil
×
293
}
294

295
// serializeInvoice serializes an invoice to a writer.
296
//
297
// Note: this function is in use for a migration. Before making changes that
298
// would modify the on disk format, make a copy of the original code and store
299
// it with the migration.
UNCOV
300
func serializeInvoice(w io.Writer, i *Invoice) error {
×
UNCOV
301
        if err := wire.WriteVarBytes(w, 0, i.Memo[:]); err != nil {
×
302
                return err
×
303
        }
×
UNCOV
304
        if err := wire.WriteVarBytes(w, 0, i.Receipt[:]); err != nil {
×
305
                return err
×
306
        }
×
UNCOV
307
        if err := wire.WriteVarBytes(w, 0, i.PaymentRequest[:]); err != nil {
×
308
                return err
×
309
        }
×
310

UNCOV
311
        if err := binary.Write(w, byteOrder, i.FinalCltvDelta); err != nil {
×
312
                return err
×
313
        }
×
314

UNCOV
315
        if err := binary.Write(w, byteOrder, int64(i.Expiry)); err != nil {
×
316
                return err
×
317
        }
×
318

UNCOV
319
        birthBytes, err := i.CreationDate.MarshalBinary()
×
UNCOV
320
        if err != nil {
×
321
                return err
×
322
        }
×
323

UNCOV
324
        if err := wire.WriteVarBytes(w, 0, birthBytes); err != nil {
×
325
                return err
×
326
        }
×
327

UNCOV
328
        settleBytes, err := i.SettleDate.MarshalBinary()
×
UNCOV
329
        if err != nil {
×
330
                return err
×
331
        }
×
332

UNCOV
333
        if err := wire.WriteVarBytes(w, 0, settleBytes); err != nil {
×
334
                return err
×
335
        }
×
336

UNCOV
337
        if _, err := w.Write(i.Terms.PaymentPreimage[:]); err != nil {
×
338
                return err
×
339
        }
×
340

UNCOV
341
        var scratch [8]byte
×
UNCOV
342
        byteOrder.PutUint64(scratch[:], uint64(i.Terms.Value))
×
UNCOV
343
        if _, err := w.Write(scratch[:]); err != nil {
×
344
                return err
×
345
        }
×
346

UNCOV
347
        if err := binary.Write(w, byteOrder, i.Terms.State); err != nil {
×
348
                return err
×
349
        }
×
350

UNCOV
351
        if err := binary.Write(w, byteOrder, i.AddIndex); err != nil {
×
352
                return err
×
353
        }
×
UNCOV
354
        if err := binary.Write(w, byteOrder, i.SettleIndex); err != nil {
×
355
                return err
×
356
        }
×
UNCOV
357
        if err := binary.Write(w, byteOrder, int64(i.AmtPaid)); err != nil {
×
358
                return err
×
359
        }
×
360

UNCOV
361
        if err := serializeHtlcs(w, i.Htlcs); err != nil {
×
362
                return err
×
363
        }
×
364

UNCOV
365
        return nil
×
366
}
367

368
// serializeHtlcs serializes a map containing circuit keys and invoice htlcs to
369
// a writer.
UNCOV
370
func serializeHtlcs(w io.Writer, htlcs map[CircuitKey]*InvoiceHTLC) error {
×
UNCOV
371
        for key, htlc := range htlcs {
×
372
                // Encode the htlc in a tlv stream.
×
373
                chanID := key.ChanID.ToUint64()
×
374
                amt := uint64(htlc.Amt)
×
375
                acceptTime := uint64(htlc.AcceptTime.UnixNano())
×
376
                resolveTime := uint64(htlc.ResolveTime.UnixNano())
×
377
                state := uint8(htlc.State)
×
378

×
379
                tlvStream, err := tlv.NewStream(
×
380
                        tlv.MakePrimitiveRecord(chanIDType, &chanID),
×
381
                        tlv.MakePrimitiveRecord(htlcIDType, &key.HtlcID),
×
382
                        tlv.MakePrimitiveRecord(amtType, &amt),
×
383
                        tlv.MakePrimitiveRecord(
×
384
                                acceptHeightType, &htlc.AcceptHeight,
×
385
                        ),
×
386
                        tlv.MakePrimitiveRecord(acceptTimeType, &acceptTime),
×
387
                        tlv.MakePrimitiveRecord(resolveTimeType, &resolveTime),
×
388
                        tlv.MakePrimitiveRecord(expiryHeightType, &htlc.Expiry),
×
389
                        tlv.MakePrimitiveRecord(stateType, &state),
×
390
                )
×
391
                if err != nil {
×
392
                        return err
×
393
                }
×
394

395
                var b bytes.Buffer
×
396
                if err := tlvStream.Encode(&b); err != nil {
×
397
                        return err
×
398
                }
×
399

400
                // Write the length of the tlv stream followed by the stream
401
                // bytes.
402
                err = binary.Write(w, byteOrder, uint64(b.Len()))
×
403
                if err != nil {
×
404
                        return err
×
405
                }
×
406

407
                if _, err := w.Write(b.Bytes()); err != nil {
×
408
                        return err
×
409
                }
×
410
        }
411

UNCOV
412
        return nil
×
413
}
414

UNCOV
415
func deserializeInvoice(r io.Reader) (Invoice, error) {
×
UNCOV
416
        var err error
×
UNCOV
417
        invoice := Invoice{}
×
UNCOV
418

×
UNCOV
419
        // TODO(roasbeef): use read full everywhere
×
UNCOV
420
        invoice.Memo, err = wire.ReadVarBytes(r, 0, MaxMemoSize, "")
×
UNCOV
421
        if err != nil {
×
422
                return invoice, err
×
423
        }
×
UNCOV
424
        invoice.Receipt, err = wire.ReadVarBytes(r, 0, MaxReceiptSize, "")
×
UNCOV
425
        if err != nil {
×
426
                return invoice, err
×
427
        }
×
428

UNCOV
429
        invoice.PaymentRequest, err = wire.ReadVarBytes(r, 0, MaxPaymentRequestSize, "")
×
UNCOV
430
        if err != nil {
×
431
                return invoice, err
×
432
        }
×
433

UNCOV
434
        if err := binary.Read(r, byteOrder, &invoice.FinalCltvDelta); err != nil {
×
435
                return invoice, err
×
436
        }
×
437

UNCOV
438
        var expiry int64
×
UNCOV
439
        if err := binary.Read(r, byteOrder, &expiry); err != nil {
×
440
                return invoice, err
×
441
        }
×
UNCOV
442
        invoice.Expiry = time.Duration(expiry)
×
UNCOV
443

×
UNCOV
444
        birthBytes, err := wire.ReadVarBytes(r, 0, 300, "birth")
×
UNCOV
445
        if err != nil {
×
446
                return invoice, err
×
447
        }
×
UNCOV
448
        if err := invoice.CreationDate.UnmarshalBinary(birthBytes); err != nil {
×
449
                return invoice, err
×
450
        }
×
451

UNCOV
452
        settledBytes, err := wire.ReadVarBytes(r, 0, 300, "settled")
×
UNCOV
453
        if err != nil {
×
454
                return invoice, err
×
455
        }
×
UNCOV
456
        if err := invoice.SettleDate.UnmarshalBinary(settledBytes); err != nil {
×
457
                return invoice, err
×
458
        }
×
459

UNCOV
460
        if _, err := io.ReadFull(r, invoice.Terms.PaymentPreimage[:]); err != nil {
×
461
                return invoice, err
×
462
        }
×
UNCOV
463
        var scratch [8]byte
×
UNCOV
464
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
×
465
                return invoice, err
×
466
        }
×
UNCOV
467
        invoice.Terms.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
×
UNCOV
468

×
UNCOV
469
        if err := binary.Read(r, byteOrder, &invoice.Terms.State); err != nil {
×
470
                return invoice, err
×
471
        }
×
472

UNCOV
473
        if err := binary.Read(r, byteOrder, &invoice.AddIndex); err != nil {
×
474
                return invoice, err
×
475
        }
×
UNCOV
476
        if err := binary.Read(r, byteOrder, &invoice.SettleIndex); err != nil {
×
477
                return invoice, err
×
478
        }
×
UNCOV
479
        if err := binary.Read(r, byteOrder, &invoice.AmtPaid); err != nil {
×
480
                return invoice, err
×
481
        }
×
482

UNCOV
483
        invoice.Htlcs, err = deserializeHtlcs(r)
×
UNCOV
484
        if err != nil {
×
485
                return Invoice{}, err
×
486
        }
×
487

UNCOV
488
        return invoice, nil
×
489
}
490

491
// deserializeHtlcs reads a list of invoice htlcs from a reader and returns it
492
// as a map.
UNCOV
493
func deserializeHtlcs(r io.Reader) (map[CircuitKey]*InvoiceHTLC, error) {
×
UNCOV
494
        htlcs := make(map[CircuitKey]*InvoiceHTLC, 0)
×
UNCOV
495

×
UNCOV
496
        for {
×
UNCOV
497
                // Read the length of the tlv stream for this htlc.
×
UNCOV
498
                var streamLen uint64
×
UNCOV
499
                if err := binary.Read(r, byteOrder, &streamLen); err != nil {
×
UNCOV
500
                        if err == io.EOF {
×
UNCOV
501
                                break
×
502
                        }
503

504
                        return nil, err
×
505
                }
506

507
                streamBytes := make([]byte, streamLen)
×
508
                if _, err := r.Read(streamBytes); err != nil {
×
509
                        return nil, err
×
510
                }
×
511
                streamReader := bytes.NewReader(streamBytes)
×
512

×
513
                // Decode the contents into the htlc fields.
×
514
                var (
×
515
                        htlc                    InvoiceHTLC
×
516
                        key                     CircuitKey
×
517
                        chanID                  uint64
×
518
                        state                   uint8
×
519
                        acceptTime, resolveTime uint64
×
520
                        amt                     uint64
×
521
                )
×
522
                tlvStream, err := tlv.NewStream(
×
523
                        tlv.MakePrimitiveRecord(chanIDType, &chanID),
×
524
                        tlv.MakePrimitiveRecord(htlcIDType, &key.HtlcID),
×
525
                        tlv.MakePrimitiveRecord(amtType, &amt),
×
526
                        tlv.MakePrimitiveRecord(
×
527
                                acceptHeightType, &htlc.AcceptHeight,
×
528
                        ),
×
529
                        tlv.MakePrimitiveRecord(acceptTimeType, &acceptTime),
×
530
                        tlv.MakePrimitiveRecord(resolveTimeType, &resolveTime),
×
531
                        tlv.MakePrimitiveRecord(expiryHeightType, &htlc.Expiry),
×
532
                        tlv.MakePrimitiveRecord(stateType, &state),
×
533
                )
×
534
                if err != nil {
×
535
                        return nil, err
×
536
                }
×
537

538
                if err := tlvStream.Decode(streamReader); err != nil {
×
539
                        return nil, err
×
540
                }
×
541

542
                key.ChanID = lnwire.NewShortChanIDFromInt(chanID)
×
543
                htlc.AcceptTime = time.Unix(0, int64(acceptTime))
×
544
                htlc.ResolveTime = time.Unix(0, int64(resolveTime))
×
545
                htlc.State = HtlcState(state)
×
546
                htlc.Amt = lnwire.MilliSatoshi(amt)
×
547

×
548
                htlcs[key] = &htlc
×
549
        }
550

UNCOV
551
        return htlcs, nil
×
552
}
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