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

lightningnetwork / lnd / 9915780197

13 Jul 2024 12:30AM UTC coverage: 49.268% (-9.1%) from 58.413%
9915780197

push

github

web-flow
Merge pull request #8653 from ProofOfKeags/fn-prim

DynComms [0/n]: `fn` package additions

92837 of 188433 relevant lines covered (49.27%)

1.55 hits per line

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

55.65
/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
        "github.com/lightningnetwork/lnd/lnwire"
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
        // fieldTypeM contains the payment metadata.
50
        fieldTypeM = 27
51

52
        // fieldTypeN contains the pubkey of the target node.
53
        fieldTypeN = 19
54

55
        // fieldTypeH contains the hash of a description of the payment.
56
        fieldTypeH = 23
57

58
        // fieldTypeX contains the expiry in seconds of the invoice.
59
        fieldTypeX = 6
60

61
        // fieldTypeF contains a fallback on-chain address.
62
        fieldTypeF = 9
63

64
        // fieldTypeR contains extra routing information.
65
        fieldTypeR = 3
66

67
        // fieldTypeC contains an optional requested final CLTV delta.
68
        fieldTypeC = 24
69

70
        // fieldType9 contains one or more bytes for signaling features
71
        // supported or required by the receiver.
72
        fieldType9 = 5
73

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

79
        // fieldTypeB contains blinded payment path information. This field may
80
        // be repeated to include multiple blinded payment paths in the invoice.
81
        fieldTypeB = 20
82

83
        // maxInvoiceLength is the maximum total length an invoice can have.
84
        // This is chosen to be the maximum number of bytes that can fit into a
85
        // single QR code: https://en.wikipedia.org/wiki/QR_code#Storage
86
        maxInvoiceLength = 7089
87

88
        // DefaultInvoiceExpiry is the default expiry duration from the creation
89
        // timestamp if expiry is set to zero.
90
        DefaultInvoiceExpiry = time.Hour
91
)
92

93
var (
94
        // ErrInvoiceTooLarge is returned when an invoice exceeds
95
        // maxInvoiceLength.
96
        ErrInvoiceTooLarge = errors.New("invoice is too large")
97

98
        // ErrInvalidFieldLength is returned when a tagged field was specified
99
        // with a length larger than the left over bytes of the data field.
100
        ErrInvalidFieldLength = errors.New("invalid field length")
101

102
        // ErrBrokenTaggedField is returned when the last tagged field is
103
        // incorrectly formatted and doesn't have enough bytes to be read.
104
        ErrBrokenTaggedField = errors.New("last tagged field is broken")
105
)
106

107
// MessageSigner is passed to the Encode method to provide a signature
108
// corresponding to the node's pubkey.
109
type MessageSigner struct {
110
        // SignCompact signs the hash of the passed msg with the node's privkey.
111
        // The returned signature should be 65 bytes, where the last 64 are the
112
        // compact signature, and the first one is a header byte. This is the
113
        // format returned by ecdsa.SignCompact.
114
        SignCompact func(msg []byte) ([]byte, error)
115
}
116

117
// Invoice represents a decoded invoice, or to-be-encoded invoice. Some of the
118
// fields are optional, and will only be non-nil if the invoice this was parsed
119
// from contains that field. When encoding, only the non-nil fields will be
120
// added to the encoded invoice.
121
type Invoice struct {
122
        // Net specifies what network this Lightning invoice is meant for.
123
        Net *chaincfg.Params
124

125
        // MilliSat specifies the amount of this invoice in millisatoshi.
126
        // Optional.
127
        MilliSat *lnwire.MilliSatoshi
128

129
        // Timestamp specifies the time this invoice was created.
130
        // Mandatory
131
        Timestamp time.Time
132

133
        // PaymentHash is the payment hash to be used for a payment to this
134
        // invoice.
135
        PaymentHash *[32]byte
136

137
        // PaymentAddr is the payment address to be used by payments to prevent
138
        // probing of the destination.
139
        PaymentAddr *[32]byte
140

141
        // Destination is the public key of the target node. This will always
142
        // be set after decoding, and can optionally be set before encoding to
143
        // include the pubkey as an 'n' field. If this is not set before
144
        // encoding then the destination pubkey won't be added as an 'n' field,
145
        // and the pubkey will be extracted from the signature during decoding.
146
        Destination *btcec.PublicKey
147

148
        // minFinalCLTVExpiry is the value that the creator of the invoice
149
        // expects to be used for the CLTV expiry of the HTLC extended to it in
150
        // the last hop.
151
        //
152
        // NOTE: This value is optional, and should be set to nil if the
153
        // invoice creator doesn't have a strong requirement on the CLTV expiry
154
        // of the final HTLC extended to it.
155
        //
156
        // This field is un-exported and can only be read by the
157
        // MinFinalCLTVExpiry() method. By forcing callers to read via this
158
        // method, we can easily enforce the default if not specified.
159
        //
160
        // NOTE: this field is ignored in the case that the invoice contains
161
        // blinded paths since then the final minimum cltv expiry delta is
162
        // expected to be included in the route's accumulated CLTV delta value.
163
        minFinalCLTVExpiry *uint64
164

165
        // Description is a short description of the purpose of this invoice.
166
        // Optional. Non-nil iff DescriptionHash is nil.
167
        Description *string
168

169
        // DescriptionHash is the SHA256 hash of a description of the purpose of
170
        // this invoice.
171
        // Optional. Non-nil iff Description is nil.
172
        DescriptionHash *[32]byte
173

174
        // expiry specifies the timespan this invoice will be valid.
175
        // Optional. If not set, a default expiry of 60 min will be implied.
176
        //
177
        // This field is unexported and can be read by the Expiry() method. This
178
        // method makes sure the default expiry time is returned in case the
179
        // field is not set.
180
        expiry *time.Duration
181

182
        // FallbackAddr is an on-chain address that can be used for payment in
183
        // case the Lightning payment fails.
184
        // Optional.
185
        FallbackAddr btcutil.Address
186

187
        // RouteHints represents one or more different route hints. Each route
188
        // hint can be individually used to reach the destination. These usually
189
        // represent private routes.
190
        //
191
        // NOTE: This is optional and should not be set at the same time as
192
        // BlindedPaymentPaths.
193
        RouteHints [][]HopHint
194

195
        // BlindedPaymentPaths is a set of blinded payment paths that can be
196
        // used to find the payment receiver.
197
        //
198
        // NOTE: This is optional and should not be set at the same time as
199
        // RouteHints.
200
        BlindedPaymentPaths []*BlindedPaymentPath
201

202
        // Features represents an optional field used to signal optional or
203
        // required support for features by the receiver.
204
        Features *lnwire.FeatureVector
205

206
        // Metadata is additional data that is sent along with the payment to
207
        // the payee.
208
        Metadata []byte
209
}
210

211
// Amount is a functional option that allows callers of NewInvoice to set the
212
// amount in millisatoshis that the Invoice should encode.
213
func Amount(milliSat lnwire.MilliSatoshi) func(*Invoice) {
3✔
214
        return func(i *Invoice) {
6✔
215
                i.MilliSat = &milliSat
3✔
216
        }
3✔
217
}
218

219
// Destination is a functional option that allows callers of NewInvoice to
220
// explicitly set the pubkey of the Invoice's destination node.
221
func Destination(destination *btcec.PublicKey) func(*Invoice) {
×
222
        return func(i *Invoice) {
×
223
                i.Destination = destination
×
224
        }
×
225
}
226

227
// Description is a functional option that allows callers of NewInvoice to set
228
// the payment description of the created Invoice.
229
//
230
// NOTE: Must be used if and only if DescriptionHash is not used.
231
func Description(description string) func(*Invoice) {
3✔
232
        return func(i *Invoice) {
6✔
233
                i.Description = &description
3✔
234
        }
3✔
235
}
236

237
// CLTVExpiry is an optional value which allows the receiver of the payment to
238
// specify the delta between the current height and the HTLC extended to the
239
// receiver.
240
func CLTVExpiry(delta uint64) func(*Invoice) {
3✔
241
        return func(i *Invoice) {
6✔
242
                i.minFinalCLTVExpiry = &delta
3✔
243
        }
3✔
244
}
245

246
// DescriptionHash is a functional option that allows callers of NewInvoice to
247
// set the payment description hash of the created Invoice.
248
//
249
// NOTE: Must be used if and only if Description is not used.
250
func DescriptionHash(descriptionHash [32]byte) func(*Invoice) {
×
251
        return func(i *Invoice) {
×
252
                i.DescriptionHash = &descriptionHash
×
253
        }
×
254
}
255

256
// Expiry is a functional option that allows callers of NewInvoice to set the
257
// expiry of the created Invoice. If not set, a default expiry of 60 min will
258
// be implied.
259
func Expiry(expiry time.Duration) func(*Invoice) {
3✔
260
        return func(i *Invoice) {
6✔
261
                i.expiry = &expiry
3✔
262
        }
3✔
263
}
264

265
// FallbackAddr is a functional option that allows callers of NewInvoice to set
266
// the Invoice's fallback on-chain address that can be used for payment in case
267
// the Lightning payment fails
268
func FallbackAddr(fallbackAddr btcutil.Address) func(*Invoice) {
×
269
        return func(i *Invoice) {
×
270
                i.FallbackAddr = fallbackAddr
×
271
        }
×
272
}
273

274
// RouteHint is a functional option that allows callers of NewInvoice to add
275
// one or more hop hints that represent a private route to the destination.
276
func RouteHint(routeHint []HopHint) func(*Invoice) {
3✔
277
        return func(i *Invoice) {
6✔
278
                i.RouteHints = append(i.RouteHints, routeHint)
3✔
279
        }
3✔
280
}
281

282
// WithBlindedPaymentPath is a functional option that allows a caller of
283
// NewInvoice to attach a blinded payment path to the invoice. The option can
284
// be used multiple times to attach multiple paths.
285
func WithBlindedPaymentPath(p *BlindedPaymentPath) func(*Invoice) {
×
286
        return func(i *Invoice) {
×
287
                i.BlindedPaymentPaths = append(i.BlindedPaymentPaths, p)
×
288
        }
×
289
}
290

291
// Features is a functional option that allows callers of NewInvoice to set the
292
// desired feature bits that are advertised on the invoice. If this option is
293
// not used, an empty feature vector will automatically be populated.
294
func Features(features *lnwire.FeatureVector) func(*Invoice) {
3✔
295
        return func(i *Invoice) {
6✔
296
                i.Features = features
3✔
297
        }
3✔
298
}
299

300
// PaymentAddr is a functional option that allows callers of NewInvoice to set
301
// the desired payment address that is advertised on the invoice.
302
func PaymentAddr(addr [32]byte) func(*Invoice) {
3✔
303
        return func(i *Invoice) {
6✔
304
                i.PaymentAddr = &addr
3✔
305
        }
3✔
306
}
307

308
// Metadata is a functional option that allows callers of NewInvoice to set
309
// the desired payment Metadata that is advertised on the invoice.
310
func Metadata(metadata []byte) func(*Invoice) {
×
311
        return func(i *Invoice) {
×
312
                i.Metadata = metadata
×
313
        }
×
314
}
315

316
// NewInvoice creates a new Invoice object. The last parameter is a set of
317
// variadic arguments for setting optional fields of the invoice.
318
//
319
// NOTE: Either Description  or DescriptionHash must be provided for the Invoice
320
// to be considered valid.
321
func NewInvoice(net *chaincfg.Params, paymentHash [32]byte,
322
        timestamp time.Time, options ...func(*Invoice)) (*Invoice, error) {
3✔
323

3✔
324
        invoice := &Invoice{
3✔
325
                Net:         net,
3✔
326
                PaymentHash: &paymentHash,
3✔
327
                Timestamp:   timestamp,
3✔
328
        }
3✔
329

3✔
330
        for _, option := range options {
6✔
331
                option(invoice)
3✔
332
        }
3✔
333

334
        // If no features were set, we'll populate an empty feature vector.
335
        if invoice.Features == nil {
3✔
336
                invoice.Features = lnwire.NewFeatureVector(
×
337
                        nil, lnwire.Features,
×
338
                )
×
339
        }
×
340

341
        if err := validateInvoice(invoice); err != nil {
3✔
342
                return nil, err
×
343
        }
×
344

345
        return invoice, nil
3✔
346
}
347

348
// Expiry returns the expiry time for this invoice. If expiry time is not set
349
// explicitly, the default 3600 second expiry will be returned.
350
func (invoice *Invoice) Expiry() time.Duration {
3✔
351
        if invoice.expiry != nil {
6✔
352
                return *invoice.expiry
3✔
353
        }
3✔
354

355
        // If no expiry is set for this invoice, default is 3600 seconds.
356
        return DefaultInvoiceExpiry
×
357
}
358

359
// MinFinalCLTVExpiry returns the minimum final CLTV expiry delta as specified
360
// by the creator of the invoice. This value specifies the delta between the
361
// current height and the expiry height of the HTLC extended in the last hop.
362
func (invoice *Invoice) MinFinalCLTVExpiry() uint64 {
3✔
363
        if invoice.minFinalCLTVExpiry != nil {
6✔
364
                return *invoice.minFinalCLTVExpiry
3✔
365
        }
3✔
366

367
        return DefaultAssumedFinalCLTVDelta
×
368
}
369

370
// validateInvoice does a sanity check of the provided Invoice, making sure it
371
// has all the necessary fields set for it to be considered valid by BOLT-0011.
372
func validateInvoice(invoice *Invoice) error {
3✔
373
        // The net must be set.
3✔
374
        if invoice.Net == nil {
3✔
375
                return fmt.Errorf("net params not set")
×
376
        }
×
377

378
        // The invoice must contain a payment hash.
379
        if invoice.PaymentHash == nil {
3✔
380
                return fmt.Errorf("no payment hash found")
×
381
        }
×
382

383
        if len(invoice.RouteHints) != 0 &&
3✔
384
                len(invoice.BlindedPaymentPaths) != 0 {
3✔
385

×
386
                return fmt.Errorf("cannot have both route hints and blinded " +
×
387
                        "payment paths")
×
388
        }
×
389

390
        // Either Description or DescriptionHash must be set, not both.
391
        if invoice.Description != nil && invoice.DescriptionHash != nil {
3✔
392
                return fmt.Errorf("both description and description hash set")
×
393
        }
×
394
        if invoice.Description == nil && invoice.DescriptionHash == nil {
3✔
395
                return fmt.Errorf("neither description nor description hash set")
×
396
        }
×
397

398
        // Check that we support the field lengths.
399
        if len(invoice.PaymentHash) != 32 {
3✔
400
                return fmt.Errorf("unsupported payment hash length: %d",
×
401
                        len(invoice.PaymentHash))
×
402
        }
×
403

404
        if invoice.DescriptionHash != nil && len(invoice.DescriptionHash) != 32 {
3✔
405
                return fmt.Errorf("unsupported description hash length: %d",
×
406
                        len(invoice.DescriptionHash))
×
407
        }
×
408

409
        if invoice.Destination != nil &&
3✔
410
                len(invoice.Destination.SerializeCompressed()) != 33 {
3✔
411
                return fmt.Errorf("unsupported pubkey length: %d",
×
412
                        len(invoice.Destination.SerializeCompressed()))
×
413
        }
×
414

415
        // Ensure that all invoices have feature vectors.
416
        if invoice.Features == nil {
3✔
417
                return fmt.Errorf("missing feature vector")
×
418
        }
×
419

420
        return nil
3✔
421
}
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