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

lightningnetwork / lnd / 15951470896

29 Jun 2025 04:23AM UTC coverage: 67.594% (-0.01%) from 67.606%
15951470896

Pull #9751

github

web-flow
Merge 599d9b051 into 6290edf14
Pull Request #9751: multi: update Go to 1.23.10 and update some packages

135088 of 199851 relevant lines covered (67.59%)

21909.44 hits per line

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

96.68
/zpay32/decode.go
1
package zpay32
2

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

11
        "github.com/btcsuite/btcd/btcec/v2"
12
        "github.com/btcsuite/btcd/btcec/v2/ecdsa"
13
        "github.com/btcsuite/btcd/btcutil"
14
        "github.com/btcsuite/btcd/btcutil/bech32"
15
        "github.com/btcsuite/btcd/chaincfg"
16
        "github.com/btcsuite/btcd/chaincfg/chainhash"
17
        "github.com/lightningnetwork/lnd/fn/v2"
18
        "github.com/lightningnetwork/lnd/lnwire"
19
)
20

21
// DecodeOption is a type that can be used to supply functional options to the
22
// Decode function.
23
type DecodeOption func(*decodeOptions)
24

25
// WithKnownFeatureBits is a functional option that overwrites the set of
26
// known feature bits. If not set, then LND's lnwire.Features variable will be
27
// used by default.
28
func WithKnownFeatureBits(features map[lnwire.FeatureBit]string) DecodeOption {
2✔
29
        return func(options *decodeOptions) {
4✔
30
                options.knownFeatureBits = features
2✔
31
        }
2✔
32
}
33

34
// WithErrorOnUnknownFeatureBit is a functional option that will cause the
35
// Decode function to return an error if the decoded invoice contains an unknown
36
// feature bit.
37
func WithErrorOnUnknownFeatureBit() DecodeOption {
1✔
38
        return func(options *decodeOptions) {
2✔
39
                options.errorOnUnknownFeature = true
1✔
40
        }
1✔
41
}
42

43
// decodeOptions holds the set of Decode options.
44
type decodeOptions struct {
45
        knownFeatureBits      map[lnwire.FeatureBit]string
46
        errorOnUnknownFeature bool
47
}
48

49
// newDecodeOptions constructs the default decodeOptions struct.
50
func newDecodeOptions() *decodeOptions {
653✔
51
        return &decodeOptions{
653✔
52
                knownFeatureBits:      lnwire.Features,
653✔
53
                errorOnUnknownFeature: false,
653✔
54
        }
653✔
55
}
653✔
56

57
// Decode parses the provided encoded invoice and returns a decoded Invoice if
58
// it is valid by BOLT-0011 and matches the provided active network.
59
func Decode(invoice string, net *chaincfg.Params, opts ...DecodeOption) (
60
        *Invoice, error) {
653✔
61

653✔
62
        options := newDecodeOptions()
653✔
63
        for _, o := range opts {
656✔
64
                o(options)
3✔
65
        }
3✔
66

67
        var decodedInvoice Invoice
653✔
68

653✔
69
        // Before bech32 decoding the invoice, make sure that it is not too large.
653✔
70
        // This is done as an anti-DoS measure since bech32 decoding is expensive.
653✔
71
        if len(invoice) > maxInvoiceLength {
656✔
72
                return nil, ErrInvoiceTooLarge
3✔
73
        }
3✔
74

75
        // Decode the invoice using the modified bech32 decoder.
76
        hrp, data, err := decodeBech32(invoice)
650✔
77
        if err != nil {
874✔
78
                return nil, err
224✔
79
        }
224✔
80

81
        // We expect the human-readable part to at least have ln + one char
82
        // encoding the network.
83
        if len(hrp) < 3 {
431✔
84
                return nil, fmt.Errorf("hrp too short")
5✔
85
        }
5✔
86

87
        // First two characters of HRP should be "ln".
88
        if hrp[:2] != "ln" {
430✔
89
                return nil, fmt.Errorf("prefix should be \"ln\"")
9✔
90
        }
9✔
91

92
        // The next characters should be a valid prefix for a segwit BIP173
93
        // address that match the active network except for signet where we add
94
        // an additional "s" to differentiate it from the older testnet3 (Core
95
        // devs decided to use the same hrp for signet as for testnet3 which is
96
        // not optimal for LN). See
97
        // https://github.com/lightningnetwork/lightning-rfc/pull/844 for more
98
        // information.
99
        expectedPrefix := net.Bech32HRPSegwit
412✔
100
        if net.Name == chaincfg.SigNetParams.Name {
464✔
101
                expectedPrefix = "tbs"
52✔
102
        }
52✔
103
        if !strings.HasPrefix(hrp[2:], expectedPrefix) {
415✔
104
                return nil, fmt.Errorf(
3✔
105
                        "invoice not for current active network '%s'", net.Name)
3✔
106
        }
3✔
107
        decodedInvoice.Net = net
409✔
108

409✔
109
        // Optionally, if there's anything left of the HRP after ln + the segwit
409✔
110
        // prefix, we try to decode this as the payment amount.
409✔
111
        var netPrefixLength = len(expectedPrefix) + 2
409✔
112
        if len(hrp) > netPrefixLength {
513✔
113
                amount, err := decodeAmount(hrp[netPrefixLength:])
104✔
114
                if err != nil {
119✔
115
                        return nil, err
15✔
116
                }
15✔
117
                decodedInvoice.MilliSat = &amount
89✔
118
        }
119

120
        // Everything except the last 520 bits of the data encodes the invoice's
121
        // timestamp and tagged fields.
122
        if len(data) < signatureBase32Len {
409✔
123
                return nil, errors.New("short invoice")
15✔
124
        }
15✔
125
        invoiceData := data[:len(data)-signatureBase32Len]
379✔
126

379✔
127
        // Parse the timestamp and tagged fields, and fill the Invoice struct.
379✔
128
        if err := parseData(&decodedInvoice, invoiceData, net); err != nil {
466✔
129
                return nil, err
87✔
130
        }
87✔
131

132
        // The last 520 bits (104 groups) make up the signature.
133
        sigBase32 := data[len(data)-signatureBase32Len:]
292✔
134
        sigBase256, err := bech32.ConvertBits(sigBase32, 5, 8, true)
292✔
135
        if err != nil {
292✔
136
                return nil, err
×
137
        }
×
138
        sig, err := lnwire.NewSigFromWireECDSA(sigBase256[:64])
292✔
139
        if err != nil {
292✔
140
                return nil, err
×
141
        }
×
142
        recoveryID := sigBase256[64]
292✔
143

292✔
144
        // The signature is over the hrp + the data the invoice, encoded in
292✔
145
        // base 256.
292✔
146
        taggedDataBytes, err := bech32.ConvertBits(invoiceData, 5, 8, true)
292✔
147
        if err != nil {
292✔
148
                return nil, err
×
149
        }
×
150

151
        toSign := append([]byte(hrp), taggedDataBytes...)
292✔
152

292✔
153
        // We expect the signature to be over the single SHA-256 hash of that
292✔
154
        // data.
292✔
155
        hash := chainhash.HashB(toSign)
292✔
156

292✔
157
        // If the destination pubkey was provided as a tagged field, use that
292✔
158
        // to verify the signature, if not do public key recovery.
292✔
159
        if decodedInvoice.Destination != nil {
318✔
160
                signature, err := sig.ToSignature()
26✔
161
                if err != nil {
30✔
162
                        return nil, fmt.Errorf("unable to deserialize "+
4✔
163
                                "signature: %v", err)
4✔
164
                }
4✔
165
                if !signature.Verify(hash, decodedInvoice.Destination) {
39✔
166
                        return nil, fmt.Errorf("invalid invoice signature")
17✔
167
                }
17✔
168
        } else {
266✔
169
                headerByte := recoveryID + 27 + 4
266✔
170
                compactSign := append([]byte{headerByte}, sig.RawBytes()...)
266✔
171
                pubkey, _, err := ecdsa.RecoverCompact(compactSign, hash)
266✔
172
                if err != nil {
345✔
173
                        return nil, err
79✔
174
                }
79✔
175
                decodedInvoice.Destination = pubkey
187✔
176
        }
177

178
        // If no feature vector was decoded, populate an empty one.
179
        if decodedInvoice.Features == nil {
341✔
180
                decodedInvoice.Features = lnwire.NewFeatureVector(
149✔
181
                        nil, options.knownFeatureBits,
149✔
182
                )
149✔
183
        }
149✔
184

185
        // Now that we have created the invoice, make sure it has the required
186
        // fields set.
187
        if err := validateInvoice(&decodedInvoice); err != nil {
259✔
188
                return nil, err
67✔
189
        }
67✔
190

191
        if options.errorOnUnknownFeature {
126✔
192
                // Make sure that we understand all the required feature bits
1✔
193
                // in the invoice.
1✔
194
                unknownFeatureBits := decodedInvoice.Features.
1✔
195
                        UnknownRequiredFeatures()
1✔
196

1✔
197
                if len(unknownFeatureBits) > 0 {
2✔
198
                        errStr := fmt.Sprintf("invoice contains " +
1✔
199
                                "unknown feature bits:")
1✔
200

1✔
201
                        for _, bit := range unknownFeatureBits {
2✔
202
                                errStr += fmt.Sprintf(" %d,", bit)
1✔
203
                        }
1✔
204

205
                        return nil, errors.New(strings.TrimRight(errStr, ","))
1✔
206
                }
207
        }
208

209
        return &decodedInvoice, nil
124✔
210
}
211

212
// parseData parses the data part of the invoice. It expects base32 data
213
// returned from the bech32.Decode method, except signature.
214
func parseData(invoice *Invoice, data []byte, net *chaincfg.Params) error {
379✔
215
        // It must contain the timestamp, encoded using 35 bits (7 groups).
379✔
216
        if len(data) < timestampBase32Len {
381✔
217
                return fmt.Errorf("data too short: %d", len(data))
2✔
218
        }
2✔
219

220
        t, err := parseTimestamp(data[:timestampBase32Len])
377✔
221
        if err != nil {
377✔
222
                return err
×
223
        }
×
224
        invoice.Timestamp = time.Unix(int64(t), 0)
377✔
225

377✔
226
        // The rest are tagged parts.
377✔
227
        tagData := data[7:]
377✔
228
        return parseTaggedFields(invoice, tagData, net)
377✔
229
}
230

231
// parseTimestamp converts a 35-bit timestamp (encoded in base32) to uint64.
232
func parseTimestamp(data []byte) (uint64, error) {
380✔
233
        if len(data) != timestampBase32Len {
382✔
234
                return 0, fmt.Errorf("timestamp must be 35 bits, was %d",
2✔
235
                        len(data)*5)
2✔
236
        }
2✔
237

238
        return base32ToUint64(data)
378✔
239
}
240

241
// parseTaggedFields takes the base32 encoded tagged fields of the invoice, and
242
// fills the Invoice struct accordingly.
243
func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) error {
386✔
244
        index := 0
386✔
245
        for len(fields)-index > 0 {
3,580✔
246
                // If there are less than 3 groups to read, there cannot be more
3,194✔
247
                // interesting information, as we need the type (1 group) and
3,194✔
248
                // length (2 groups).
3,194✔
249
                //
3,194✔
250
                // This means the last tagged field is broken.
3,194✔
251
                if len(fields)-index < 3 {
3,198✔
252
                        return ErrBrokenTaggedField
4✔
253
                }
4✔
254

255
                typ := fields[index]
3,190✔
256
                dataLength, err := parseFieldDataLength(fields[index+1 : index+3])
3,190✔
257
                if err != nil {
3,190✔
258
                        return err
×
259
                }
×
260

261
                // If we don't have enough field data left to read this length,
262
                // return error.
263
                if len(fields) < index+3+int(dataLength) {
3,239✔
264
                        return ErrInvalidFieldLength
49✔
265
                }
49✔
266
                base32Data := fields[index+3 : index+3+int(dataLength)]
3,141✔
267

3,141✔
268
                // Advance the index in preparation for the next iteration.
3,141✔
269
                index += 3 + int(dataLength)
3,141✔
270

3,141✔
271
                switch typ {
3,141✔
272
                case fieldTypeP:
741✔
273
                        if invoice.PaymentHash != nil {
1,010✔
274
                                // We skip the field if we have already seen a
269✔
275
                                // supported one.
269✔
276
                                continue
269✔
277
                        }
278

279
                        invoice.PaymentHash, err = parse32Bytes(base32Data)
472✔
280

281
                case fieldTypeS:
52✔
282
                        if invoice.PaymentAddr.IsSome() {
65✔
283
                                // We skip the field if we have already seen a
13✔
284
                                // supported one.
13✔
285
                                continue
13✔
286
                        }
287

288
                        addr, err := parse32Bytes(base32Data)
39✔
289
                        if err != nil {
40✔
290
                                return err
1✔
291
                        }
1✔
292
                        if addr != nil {
58✔
293
                                invoice.PaymentAddr = fn.Some(*addr)
20✔
294
                        }
20✔
295

296
                case fieldTypeD:
503✔
297
                        if invoice.Description != nil {
888✔
298
                                // We skip the field if we have already seen a
385✔
299
                                // supported one.
385✔
300
                                continue
385✔
301
                        }
302

303
                        invoice.Description, err = parseDescription(base32Data)
118✔
304

305
                case fieldTypeM:
19✔
306
                        if invoice.Metadata != nil {
27✔
307
                                // We skip the field if we have already seen a
8✔
308
                                // supported one.
8✔
309
                                continue
8✔
310
                        }
311

312
                        invoice.Metadata, err = parseMetadata(base32Data)
11✔
313

314
                case fieldTypeN:
64✔
315
                        if invoice.Destination != nil {
80✔
316
                                // We skip the field if we have already seen a
16✔
317
                                // supported one.
16✔
318
                                continue
16✔
319
                        }
320

321
                        invoice.Destination, err = parseDestination(base32Data)
48✔
322

323
                case fieldTypeH:
61✔
324
                        if invoice.DescriptionHash != nil {
69✔
325
                                // We skip the field if we have already seen a
8✔
326
                                // supported one.
8✔
327
                                continue
8✔
328
                        }
329

330
                        invoice.DescriptionHash, err = parse32Bytes(base32Data)
53✔
331

332
                case fieldTypeX:
94✔
333
                        if invoice.expiry != nil {
163✔
334
                                // We skip the field if we have already seen a
69✔
335
                                // supported one.
69✔
336
                                continue
69✔
337
                        }
338

339
                        invoice.expiry, err = parseExpiry(base32Data)
25✔
340

341
                case fieldTypeC:
286✔
342
                        if invoice.minFinalCLTVExpiry != nil {
548✔
343
                                // We skip the field if we have already seen a
262✔
344
                                // supported one.
262✔
345
                                continue
262✔
346
                        }
347

348
                        invoice.minFinalCLTVExpiry, err =
24✔
349
                                parseMinFinalCLTVExpiry(base32Data)
24✔
350

351
                case fieldTypeF:
100✔
352
                        if invoice.FallbackAddr != nil {
136✔
353
                                // We skip the field if we have already seen a
36✔
354
                                // supported one.
36✔
355
                                continue
36✔
356
                        }
357

358
                        invoice.FallbackAddr, err = parseFallbackAddr(
64✔
359
                                base32Data, net,
64✔
360
                        )
64✔
361

362
                case fieldTypeR:
595✔
363
                        // An `r` field can be included in an invoice multiple
595✔
364
                        // times, so we won't skip it if we have already seen
595✔
365
                        // one.
595✔
366
                        routeHint, err := parseRouteHint(base32Data)
595✔
367
                        if err != nil {
602✔
368
                                return err
7✔
369
                        }
7✔
370

371
                        invoice.RouteHints = append(
588✔
372
                                invoice.RouteHints, routeHint,
588✔
373
                        )
588✔
374

375
                case fieldType9:
136✔
376
                        if invoice.Features != nil {
214✔
377
                                // We skip the field if we have already seen a
78✔
378
                                // supported one.
78✔
379
                                continue
78✔
380
                        }
381

382
                        invoice.Features, err = parseFeatures(base32Data)
58✔
383

384
                case fieldTypeB:
19✔
385
                        blindedPaymentPath, err := parseBlindedPaymentPath(
19✔
386
                                base32Data,
19✔
387
                        )
19✔
388
                        if err != nil {
33✔
389
                                return err
14✔
390
                        }
14✔
391

392
                        invoice.BlindedPaymentPaths = append(
5✔
393
                                invoice.BlindedPaymentPaths, blindedPaymentPath,
5✔
394
                        )
5✔
395

396
                default:
492✔
397
                        // Ignore unknown type.
398
                }
399

400
                // Check if there was an error from parsing any of the tagged
401
                // fields and return it.
402
                if err != nil {
1,990✔
403
                        return err
15✔
404
                }
15✔
405
        }
406

407
        return nil
296✔
408
}
409

410
// parseFieldDataLength converts the two byte slice into a uint16.
411
func parseFieldDataLength(data []byte) (uint16, error) {
3,197✔
412
        if len(data) != 2 {
3,200✔
413
                return 0, fmt.Errorf("data length must be 2 bytes, was %d",
3✔
414
                        len(data))
3✔
415
        }
3✔
416

417
        return uint16(data[0])<<5 | uint16(data[1]), nil
3,194✔
418
}
419

420
// parse32Bytes converts a 256-bit value (encoded in base32) to *[32]byte. This
421
// can be used for payment hashes, description hashes, payment addresses, etc.
422
func parse32Bytes(data []byte) (*[32]byte, error) {
565✔
423
        var paymentHash [32]byte
565✔
424

565✔
425
        // As BOLT-11 states, a reader must skip over the 32-byte fields if
565✔
426
        // it does not have a length of 52, so avoid returning an error.
565✔
427
        if len(data) != hashBase32Len {
936✔
428
                return nil, nil
371✔
429
        }
371✔
430

431
        hash, err := bech32.ConvertBits(data, 5, 8, false)
194✔
432
        if err != nil {
196✔
433
                return nil, err
2✔
434
        }
2✔
435

436
        copy(paymentHash[:], hash)
192✔
437

192✔
438
        return &paymentHash, nil
192✔
439
}
440

441
// parseDescription converts the data (encoded in base32) into a string to use
442
// as the description.
443
func parseDescription(data []byte) (*string, error) {
121✔
444
        base256Data, err := bech32.ConvertBits(data, 5, 8, false)
121✔
445
        if err != nil {
123✔
446
                return nil, err
2✔
447
        }
2✔
448

449
        description := string(base256Data)
119✔
450

119✔
451
        return &description, nil
119✔
452
}
453

454
// parseMetadata converts the data (encoded in base32) into a byte slice to use
455
// as the metadata.
456
func parseMetadata(data []byte) ([]byte, error) {
11✔
457
        return bech32.ConvertBits(data, 5, 8, false)
11✔
458
}
11✔
459

460
// parseDestination converts the data (encoded in base32) into a 33-byte public
461
// key of the payee node.
462
func parseDestination(data []byte) (*btcec.PublicKey, error) {
52✔
463
        // As BOLT-11 states, a reader must skip over the destination field
52✔
464
        // if it does not have a length of 53, so avoid returning an error.
52✔
465
        if len(data) != pubKeyBase32Len {
71✔
466
                return nil, nil
19✔
467
        }
19✔
468

469
        base256Data, err := bech32.ConvertBits(data, 5, 8, false)
33✔
470
        if err != nil {
34✔
471
                return nil, err
1✔
472
        }
1✔
473

474
        return btcec.ParsePubKey(base256Data)
32✔
475
}
476

477
// parseExpiry converts the data (encoded in base32) into the expiry time.
478
func parseExpiry(data []byte) (*time.Duration, error) {
28✔
479
        expiry, err := base32ToUint64(data)
28✔
480
        if err != nil {
30✔
481
                return nil, err
2✔
482
        }
2✔
483

484
        duration := time.Duration(expiry) * time.Second
26✔
485

26✔
486
        return &duration, nil
26✔
487
}
488

489
// parseMinFinalCLTVExpiry converts the data (encoded in base32) into a uint64
490
// to use as the minFinalCLTVExpiry.
491
func parseMinFinalCLTVExpiry(data []byte) (*uint64, error) {
28✔
492
        expiry, err := base32ToUint64(data)
28✔
493
        if err != nil {
30✔
494
                return nil, err
2✔
495
        }
2✔
496

497
        return &expiry, nil
26✔
498
}
499

500
// parseFallbackAddr converts the data (encoded in base32) into a fallback
501
// on-chain address.
502
func parseFallbackAddr(data []byte, net *chaincfg.Params) (btcutil.Address, error) { // nolint:dupl
71✔
503
        // Checks if the data is empty or contains a version without an address.
71✔
504
        if len(data) < 2 {
75✔
505
                return nil, fmt.Errorf("empty fallback address field")
4✔
506
        }
4✔
507

508
        var addr btcutil.Address
67✔
509

67✔
510
        version := data[0]
67✔
511
        switch version {
67✔
512
        case 0:
9✔
513
                witness, err := bech32.ConvertBits(data[1:], 5, 8, false)
9✔
514
                if err != nil {
10✔
515
                        return nil, err
1✔
516
                }
1✔
517

518
                switch len(witness) {
8✔
519
                case 20:
4✔
520
                        addr, err = btcutil.NewAddressWitnessPubKeyHash(witness, net)
4✔
521
                case 32:
3✔
522
                        addr, err = btcutil.NewAddressWitnessScriptHash(witness, net)
3✔
523
                default:
1✔
524
                        return nil, fmt.Errorf("unknown witness program length %d",
1✔
525
                                len(witness))
1✔
526
                }
527

528
                if err != nil {
7✔
529
                        return nil, err
×
530
                }
×
531
        case 17:
16✔
532
                pubKeyHash, err := bech32.ConvertBits(data[1:], 5, 8, false)
16✔
533
                if err != nil {
17✔
534
                        return nil, err
1✔
535
                }
1✔
536

537
                addr, err = btcutil.NewAddressPubKeyHash(pubKeyHash, net)
15✔
538
                if err != nil {
16✔
539
                        return nil, err
1✔
540
                }
1✔
541
        case 18:
6✔
542
                scriptHash, err := bech32.ConvertBits(data[1:], 5, 8, false)
6✔
543
                if err != nil {
7✔
544
                        return nil, err
1✔
545
                }
1✔
546

547
                addr, err = btcutil.NewAddressScriptHashFromHash(scriptHash, net)
5✔
548
                if err != nil {
6✔
549
                        return nil, err
1✔
550
                }
1✔
551
        default:
36✔
552
                // Ignore unknown version.
553
        }
554

555
        return addr, nil
61✔
556
}
557

558
// parseRouteHint converts the data (encoded in base32) into an array containing
559
// one or more routing hop hints that represent a single route hint.
560
func parseRouteHint(data []byte) ([]HopHint, error) {
600✔
561
        base256Data, err := bech32.ConvertBits(data, 5, 8, false)
600✔
562
        if err != nil {
605✔
563
                return nil, err
5✔
564
        }
5✔
565

566
        // Check that base256Data is a multiple of hopHintLen.
567
        if len(base256Data)%hopHintLen != 0 {
598✔
568
                return nil, fmt.Errorf("expected length multiple of %d bytes, "+
3✔
569
                        "got %d", hopHintLen, len(base256Data))
3✔
570
        }
3✔
571

572
        var routeHint []HopHint
592✔
573

592✔
574
        for len(base256Data) > 0 {
641✔
575
                hopHint := HopHint{}
49✔
576
                hopHint.NodeID, err = btcec.ParsePubKey(base256Data[:33])
49✔
577
                if err != nil {
50✔
578
                        return nil, err
1✔
579
                }
1✔
580
                hopHint.ChannelID = binary.BigEndian.Uint64(base256Data[33:41])
48✔
581
                hopHint.FeeBaseMSat = binary.BigEndian.Uint32(base256Data[41:45])
48✔
582
                hopHint.FeeProportionalMillionths = binary.BigEndian.Uint32(base256Data[45:49])
48✔
583
                hopHint.CLTVExpiryDelta = binary.BigEndian.Uint16(base256Data[49:51])
48✔
584

48✔
585
                routeHint = append(routeHint, hopHint)
48✔
586

48✔
587
                base256Data = base256Data[51:]
48✔
588
        }
589

590
        return routeHint, nil
591✔
591
}
592

593
// parseBlindedPaymentPath attempts to parse a BlindedPaymentPath from the given
594
// byte slice.
595
func parseBlindedPaymentPath(data []byte) (*BlindedPaymentPath, error) {
19✔
596
        base256Data, err := bech32.ConvertBits(data, 5, 8, false)
19✔
597
        if err != nil {
20✔
598
                return nil, err
1✔
599
        }
1✔
600

601
        return DecodeBlindedPayment(bytes.NewReader(base256Data))
18✔
602
}
603

604
// parseFeatures decodes any feature bits directly from the base32
605
// representation.
606
func parseFeatures(data []byte) (*lnwire.FeatureVector, error) {
58✔
607
        rawFeatures := lnwire.NewRawFeatureVector()
58✔
608
        err := rawFeatures.DecodeBase32(bytes.NewReader(data), len(data))
58✔
609
        if err != nil {
58✔
610
                return nil, err
×
611
        }
×
612

613
        return lnwire.NewFeatureVector(rawFeatures, lnwire.Features), nil
58✔
614
}
615

616
// base32ToUint64 converts a base32 encoded number to uint64.
617
func base32ToUint64(data []byte) (uint64, error) {
429✔
618
        // Maximum that fits in uint64 is ceil(64 / 5) = 12 groups.
429✔
619
        if len(data) > 13 {
433✔
620
                return 0, fmt.Errorf("cannot parse data of length %d as uint64",
4✔
621
                        len(data))
4✔
622
        }
4✔
623

624
        val := uint64(0)
425✔
625
        for i := 0; i < len(data); i++ {
3,172✔
626
                val = val<<5 | uint64(data[i])
2,747✔
627
        }
2,747✔
628
        return val, nil
425✔
629
}
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