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

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

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/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