• 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/zpay32/invoice.go
1
package zpay32
2

3
import (
4
        "errors"
5
        "fmt"
6
        "time"
7

8
        "github.com/btcsuite/btcd/btcec/v2"
9
        "github.com/btcsuite/btcd/btcutil"
10
        "github.com/btcsuite/btcd/chaincfg"
11
        lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
12
)
13

14
const (
15
        // mSatPerBtc is the number of millisatoshis in 1 BTC.
16
        mSatPerBtc = 100000000000
17

18
        // signatureBase32Len is the number of 5-bit groups needed to encode
19
        // the 512 bit signature + 8 bit recovery ID.
20
        signatureBase32Len = 104
21

22
        // timestampBase32Len is the number of 5-bit groups needed to encode
23
        // the 35-bit timestamp.
24
        timestampBase32Len = 7
25

26
        // hashBase32Len is the number of 5-bit groups needed to encode a
27
        // 256-bit hash. Note that the last group will be padded with zeroes.
28
        hashBase32Len = 52
29

30
        // pubKeyBase32Len is the number of 5-bit groups needed to encode a
31
        // 33-byte compressed pubkey. Note that the last group will be padded
32
        // with zeroes.
33
        pubKeyBase32Len = 53
34

35
        // hopHintLen is the number of bytes needed to encode the hop hint of a
36
        // single private route.
37
        hopHintLen = 51
38

39
        // The following byte values correspond to the supported field types.
40
        // The field name is the character representing that 5-bit value in the
41
        // bech32 string.
42

43
        // fieldTypeP is the field containing the payment hash.
44
        fieldTypeP = 1
45

46
        // fieldTypeD contains a short description of the payment.
47
        fieldTypeD = 13
48

49
        // fieldTypeN contains the pubkey of the target node.
50
        fieldTypeN = 19
51

52
        // fieldTypeH contains the hash of a description of the payment.
53
        fieldTypeH = 23
54

55
        // fieldTypeX contains the expiry in seconds of the invoice.
56
        fieldTypeX = 6
57

58
        // fieldTypeF contains a fallback on-chain address.
59
        fieldTypeF = 9
60

61
        // fieldTypeR contains extra routing information.
62
        fieldTypeR = 3
63

64
        // fieldTypeC contains an optional requested final CLTV delta.
65
        fieldTypeC = 24
66

67
        // fieldType9 contains one or more bytes for signaling features
68
        // supported or required by the receiver.
69
        fieldType9 = 5
70

71
        // fieldTypeS contains a 32-byte payment address, which is a nonce
72
        // included in the final hop's payload to prevent intermediaries from
73
        // probing the recipient.
74
        fieldTypeS = 16
75

76
        // maxInvoiceLength is the maximum total length an invoice can have.
77
        // This is chosen to be the maximum number of bytes that can fit into a
78
        // single QR code: https://en.wikipedia.org/wiki/QR_code#Storage
79
        maxInvoiceLength = 7089
80

81
        // DefaultInvoiceExpiry is the default expiry duration from the creation
82
        // timestamp if expiry is set to zero.
83
        DefaultInvoiceExpiry = time.Hour
84
)
85

86
var (
87
        // ErrInvoiceTooLarge is returned when an invoice exceeds
88
        // maxInvoiceLength.
89
        ErrInvoiceTooLarge = errors.New("invoice is too large")
90

91
        // ErrInvalidFieldLength is returned when a tagged field was specified
92
        // with a length larger than the left over bytes of the data field.
93
        ErrInvalidFieldLength = errors.New("invalid field length")
94

95
        // ErrBrokenTaggedField is returned when the last tagged field is
96
        // incorrectly formatted and doesn't have enough bytes to be read.
97
        ErrBrokenTaggedField = errors.New("last tagged field is broken")
98
)
99

100
// MessageSigner is passed to the Encode method to provide a signature
101
// corresponding to the node's pubkey.
102
type MessageSigner struct {
103
        // SignCompact signs the passed hash with the node's privkey. The
104
        // returned signature should be 65 bytes, where the last 64 are the
105
        // compact signature, and the first one is a header byte. This is the
106
        // format returned by ecdsa.SignCompact.
107
        SignCompact func(hash []byte) ([]byte, error)
108
}
109

110
// Invoice represents a decoded invoice, or to-be-encoded invoice. Some of the
111
// fields are optional, and will only be non-nil if the invoice this was parsed
112
// from contains that field. When encoding, only the non-nil fields will be
113
// added to the encoded invoice.
114
type Invoice struct {
115
        // Net specifies what network this Lightning invoice is meant for.
116
        Net *chaincfg.Params
117

118
        // MilliSat specifies the amount of this invoice in millisatoshi.
119
        // Optional.
120
        MilliSat *lnwire.MilliSatoshi
121

122
        // Timestamp specifies the time this invoice was created.
123
        // Mandatory
124
        Timestamp time.Time
125

126
        // PaymentHash is the payment hash to be used for a payment to this
127
        // invoice.
128
        PaymentHash *[32]byte
129

130
        // PaymentAddr is the payment address to be used by payments to prevent
131
        // probing of the destination.
132
        PaymentAddr *[32]byte
133

134
        // Destination is the public key of the target node. This will always
135
        // be set after decoding, and can optionally be set before encoding to
136
        // include the pubkey as an 'n' field. If this is not set before
137
        // encoding then the destination pubkey won't be added as an 'n' field,
138
        // and the pubkey will be extracted from the signature during decoding.
139
        Destination *btcec.PublicKey
140

141
        // minFinalCLTVExpiry is the value that the creator of the invoice
142
        // expects to be used for the CLTV expiry of the HTLC extended to it in
143
        // the last hop.
144
        //
145
        // NOTE: This value is optional, and should be set to nil if the
146
        // invoice creator doesn't have a strong requirement on the CLTV expiry
147
        // of the final HTLC extended to it.
148
        //
149
        // This field is un-exported and can only be read by the
150
        // MinFinalCLTVExpiry() method. By forcing callers to read via this
151
        // method, we can easily enforce the default if not specified.
152
        minFinalCLTVExpiry *uint64
153

154
        // Description is a short description of the purpose of this invoice.
155
        // Optional. Non-nil iff DescriptionHash is nil.
156
        Description *string
157

158
        // DescriptionHash is the SHA256 hash of a description of the purpose of
159
        // this invoice.
160
        // Optional. Non-nil iff Description is nil.
161
        DescriptionHash *[32]byte
162

163
        // expiry specifies the timespan this invoice will be valid.
164
        // Optional. If not set, a default expiry of 60 min will be implied.
165
        //
166
        // This field is unexported and can be read by the Expiry() method. This
167
        // method makes sure the default expiry time is returned in case the
168
        // field is not set.
169
        expiry *time.Duration
170

171
        // FallbackAddr is an on-chain address that can be used for payment in
172
        // case the Lightning payment fails.
173
        // Optional.
174
        FallbackAddr btcutil.Address
175

176
        // RouteHints represents one or more different route hints. Each route
177
        // hint can be individually used to reach the destination. These usually
178
        // represent private routes.
179
        //
180
        // NOTE: This is optional.
181
        RouteHints [][]HopHint
182

183
        // Features represents an optional field used to signal optional or
184
        // required support for features by the receiver.
185
        Features *lnwire.FeatureVector
186
}
187

188
// Amount is a functional option that allows callers of NewInvoice to set the
189
// amount in millisatoshis that the Invoice should encode.
190
func Amount(milliSat lnwire.MilliSatoshi) func(*Invoice) {
×
191
        return func(i *Invoice) {
×
192
                i.MilliSat = &milliSat
×
193
        }
×
194
}
195

196
// Destination is a functional option that allows callers of NewInvoice to
197
// explicitly set the pubkey of the Invoice's destination node.
198
func Destination(destination *btcec.PublicKey) func(*Invoice) {
×
199
        return func(i *Invoice) {
×
200
                i.Destination = destination
×
201
        }
×
202
}
203

204
// Description is a functional option that allows callers of NewInvoice to set
205
// the payment description of the created Invoice.
206
//
207
// NOTE: Must be used if and only if DescriptionHash is not used.
208
func Description(description string) func(*Invoice) {
×
209
        return func(i *Invoice) {
×
210
                i.Description = &description
×
211
        }
×
212
}
213

214
// CLTVExpiry is an optional value which allows the receiver of the payment to
215
// specify the delta between the current height and the HTLC extended to the
216
// receiver.
217
func CLTVExpiry(delta uint64) func(*Invoice) {
×
218
        return func(i *Invoice) {
×
219
                i.minFinalCLTVExpiry = &delta
×
220
        }
×
221
}
222

223
// DescriptionHash is a functional option that allows callers of NewInvoice to
224
// set the payment description hash of the created Invoice.
225
//
226
// NOTE: Must be used if and only if Description is not used.
227
func DescriptionHash(descriptionHash [32]byte) func(*Invoice) {
×
228
        return func(i *Invoice) {
×
229
                i.DescriptionHash = &descriptionHash
×
230
        }
×
231
}
232

233
// Expiry is a functional option that allows callers of NewInvoice to set the
234
// expiry of the created Invoice. If not set, a default expiry of 60 min will
235
// be implied.
236
func Expiry(expiry time.Duration) func(*Invoice) {
×
237
        return func(i *Invoice) {
×
238
                i.expiry = &expiry
×
239
        }
×
240
}
241

242
// FallbackAddr is a functional option that allows callers of NewInvoice to set
243
// the Invoice's fallback on-chain address that can be used for payment in case
244
// the Lightning payment fails
245
func FallbackAddr(fallbackAddr btcutil.Address) func(*Invoice) {
×
246
        return func(i *Invoice) {
×
247
                i.FallbackAddr = fallbackAddr
×
248
        }
×
249
}
250

251
// RouteHint is a functional option that allows callers of NewInvoice to add
252
// one or more hop hints that represent a private route to the destination.
253
func RouteHint(routeHint []HopHint) func(*Invoice) {
×
254
        return func(i *Invoice) {
×
255
                i.RouteHints = append(i.RouteHints, routeHint)
×
256
        }
×
257
}
258

259
// Features is a functional option that allows callers of NewInvoice to set the
260
// desired feature bits that are advertised on the invoice. If this option is
261
// not used, an empty feature vector will automatically be populated.
262
func Features(features *lnwire.FeatureVector) func(*Invoice) {
×
263
        return func(i *Invoice) {
×
264
                i.Features = features
×
265
        }
×
266
}
267

268
// PaymentAddr is a functional option that allows callers of NewInvoice to set
269
// the desired payment address that is advertised on the invoice.
270
func PaymentAddr(addr [32]byte) func(*Invoice) {
×
271
        return func(i *Invoice) {
×
272
                i.PaymentAddr = &addr
×
273
        }
×
274
}
275

276
// NewInvoice creates a new Invoice object. The last parameter is a set of
277
// variadic arguments for setting optional fields of the invoice.
278
//
279
// NOTE: Either Description  or DescriptionHash must be provided for the Invoice
280
// to be considered valid.
281
func NewInvoice(net *chaincfg.Params, paymentHash [32]byte,
282
        timestamp time.Time, options ...func(*Invoice)) (*Invoice, error) {
×
283

×
284
        invoice := &Invoice{
×
285
                Net:         net,
×
286
                PaymentHash: &paymentHash,
×
287
                Timestamp:   timestamp,
×
288
        }
×
289

×
290
        for _, option := range options {
×
291
                option(invoice)
×
292
        }
×
293

294
        // If no features were set, we'll populate an empty feature vector.
295
        if invoice.Features == nil {
×
296
                invoice.Features = lnwire.NewFeatureVector(
×
297
                        nil, lnwire.Features,
×
298
                )
×
299
        }
×
300

301
        if err := validateInvoice(invoice); err != nil {
×
302
                return nil, err
×
303
        }
×
304

305
        return invoice, nil
×
306
}
307

308
// Expiry returns the expiry time for this invoice. If expiry time is not set
309
// explicitly, the default 3600 second expiry will be returned.
UNCOV
310
func (invoice *Invoice) Expiry() time.Duration {
×
UNCOV
311
        if invoice.expiry != nil {
×
312
                return *invoice.expiry
×
313
        }
×
314

315
        // If no expiry is set for this invoice, default is 3600 seconds.
UNCOV
316
        return DefaultInvoiceExpiry
×
317
}
318

319
// MinFinalCLTVExpiry returns the minimum final CLTV expiry delta as specified
320
// by the creator of the invoice. This value specifies the delta between the
321
// current height and the expiry height of the HTLC extended in the last hop.
UNCOV
322
func (invoice *Invoice) MinFinalCLTVExpiry() uint64 {
×
UNCOV
323
        if invoice.minFinalCLTVExpiry != nil {
×
UNCOV
324
                return *invoice.minFinalCLTVExpiry
×
UNCOV
325
        }
×
326

327
        return DefaultFinalCLTVDelta
×
328
}
329

330
// validateInvoice does a sanity check of the provided Invoice, making sure it
331
// has all the necessary fields set for it to be considered valid by BOLT-0011.
UNCOV
332
func validateInvoice(invoice *Invoice) error {
×
UNCOV
333
        // The net must be set.
×
UNCOV
334
        if invoice.Net == nil {
×
335
                return fmt.Errorf("net params not set")
×
336
        }
×
337

338
        // The invoice must contain a payment hash.
UNCOV
339
        if invoice.PaymentHash == nil {
×
340
                return fmt.Errorf("no payment hash found")
×
341
        }
×
342

343
        // Either Description or DescriptionHash must be set, not both.
UNCOV
344
        if invoice.Description != nil && invoice.DescriptionHash != nil {
×
345
                return fmt.Errorf("both description and description hash set")
×
346
        }
×
UNCOV
347
        if invoice.Description == nil && invoice.DescriptionHash == nil {
×
348
                return fmt.Errorf("neither description nor description hash set")
×
349
        }
×
350

351
        // Check that we support the field lengths.
UNCOV
352
        if len(invoice.PaymentHash) != 32 {
×
353
                return fmt.Errorf("unsupported payment hash length: %d",
×
354
                        len(invoice.PaymentHash))
×
355
        }
×
356

UNCOV
357
        if invoice.DescriptionHash != nil && len(invoice.DescriptionHash) != 32 {
×
358
                return fmt.Errorf("unsupported description hash length: %d",
×
359
                        len(invoice.DescriptionHash))
×
360
        }
×
361

UNCOV
362
        if invoice.Destination != nil &&
×
UNCOV
363
                len(invoice.Destination.SerializeCompressed()) != 33 {
×
364
                return fmt.Errorf("unsupported pubkey length: %d",
×
365
                        len(invoice.Destination.SerializeCompressed()))
×
366
        }
×
367

368
        // Ensure that all invoices have feature vectors.
UNCOV
369
        if invoice.Features == nil {
×
370
                return fmt.Errorf("missing feature vector")
×
371
        }
×
372

UNCOV
373
        return nil
×
374
}
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