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

lightningnetwork / lnd / 14388908780

10 Apr 2025 07:39PM UTC coverage: 56.811% (-12.3%) from 69.08%
14388908780

Pull #9702

github

web-flow
Merge f006bbf4d into b732525a9
Pull Request #9702: multi: make payment address mandatory

28 of 42 new or added lines in 11 files covered. (66.67%)

23231 existing lines in 283 files now uncovered.

107286 of 188846 relevant lines covered (56.81%)

22749.28 hits per line

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

73.95
/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) {
98✔
214
        return func(i *Invoice) {
196✔
215
                i.MilliSat = &milliSat
98✔
216
        }
98✔
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) {
7✔
222
        return func(i *Invoice) {
14✔
223
                i.Destination = destination
7✔
224
        }
7✔
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) {
103✔
232
        return func(i *Invoice) {
206✔
233
                i.Description = &description
103✔
234
        }
103✔
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) {
4✔
241
        return func(i *Invoice) {
8✔
242
                i.minFinalCLTVExpiry = &delta
4✔
243
        }
4✔
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) {
2✔
251
        return func(i *Invoice) {
4✔
252
                i.DescriptionHash = &descriptionHash
2✔
253
        }
2✔
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) {
91✔
260
        return func(i *Invoice) {
182✔
261
                i.expiry = &expiry
91✔
262
        }
91✔
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) {
1✔
269
        return func(i *Invoice) {
2✔
270
                i.FallbackAddr = fallbackAddr
1✔
271
        }
1✔
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) {
1✔
277
        return func(i *Invoice) {
2✔
278
                i.RouteHints = append(i.RouteHints, routeHint)
1✔
279
        }
1✔
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) {
2✔
286
        return func(i *Invoice) {
4✔
287
                i.BlindedPaymentPaths = append(i.BlindedPaymentPaths, p)
2✔
288
        }
2✔
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.
UNCOV
294
func Features(features *lnwire.FeatureVector) func(*Invoice) {
×
UNCOV
295
        return func(i *Invoice) {
×
UNCOV
296
                i.Features = features
×
UNCOV
297
        }
×
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) {
1✔
303
        return func(i *Invoice) {
2✔
304
                i.PaymentAddr = &addr
1✔
305
        }
1✔
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, paymentAddr [32]byte, options ...func(*Invoice)) (*Invoice, error) {
104✔
323

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

104✔
331
        for _, option := range options {
414✔
332
                option(invoice)
310✔
333
        }
310✔
334

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

342
        if err := validateInvoice(invoice); err != nil {
105✔
343
                return nil, err
1✔
344
        }
1✔
345

346
        return invoice, nil
103✔
347
}
348

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

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

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

UNCOV
368
        return DefaultAssumedFinalCLTVDelta
×
369
}
370

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

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

384
        // The invoice must contain a payment address (payment secret).
385
        if invoice.PaymentAddr == nil {
452✔
386
                return fmt.Errorf("no payment address found")
95✔
387
        }
95✔
388

389
        if len(invoice.RouteHints) != 0 &&
262✔
390
                len(invoice.BlindedPaymentPaths) != 0 {
262✔
391

×
392
                return fmt.Errorf("cannot have both route hints and blinded " +
×
393
                        "payment paths")
×
394
        }
×
395

396
        // Either Description or DescriptionHash must be set, not both.
397
        if invoice.Description != nil && invoice.DescriptionHash != nil {
264✔
398
                return fmt.Errorf("both description and description hash set")
2✔
399
        }
2✔
400
        if invoice.Description == nil && invoice.DescriptionHash == nil {
261✔
401
                return fmt.Errorf("neither description nor description hash set")
1✔
402
        }
1✔
403

404
        // Check that we support the field lengths.
405
        if len(invoice.PaymentHash) != 32 {
259✔
406
                return fmt.Errorf("unsupported payment hash length: %d",
×
407
                        len(invoice.PaymentHash))
×
408
        }
×
409

410
        if invoice.DescriptionHash != nil && len(invoice.DescriptionHash) != 32 {
259✔
411
                return fmt.Errorf("unsupported description hash length: %d",
×
412
                        len(invoice.DescriptionHash))
×
413
        }
×
414

415
        if invoice.Destination != nil &&
259✔
416
                len(invoice.Destination.SerializeCompressed()) != 33 {
259✔
417
                return fmt.Errorf("unsupported pubkey length: %d",
×
418
                        len(invoice.Destination.SerializeCompressed()))
×
419
        }
×
420

421
        // Ensure that all invoices have feature vectors.
422
        if invoice.Features == nil {
259✔
423
                return fmt.Errorf("missing feature vector")
×
424
        }
×
425

426
        return nil
259✔
427
}
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