• 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

70.42
/zpay32/encode.go
1
package zpay32
2

3
import (
4
        "bytes"
5
        "encoding/binary"
6
        "fmt"
7

8
        "github.com/btcsuite/btcd/btcutil"
9
        "github.com/btcsuite/btcd/btcutil/bech32"
10
        "github.com/btcsuite/btcd/chaincfg"
11
        "github.com/btcsuite/btcd/chaincfg/chainhash"
12
        "github.com/lightningnetwork/lnd/lnwire"
13
)
14

15
// Encode takes the given MessageSigner and returns a string encoding this
16
// invoice signed by the node key of the signer.
17
func (invoice *Invoice) Encode(signer MessageSigner) (string, error) {
127✔
18
        // First check that this invoice is valid before starting the encoding.
127✔
19
        if err := validateInvoice(invoice); err != nil {
130✔
20
                return "", err
3✔
21
        }
3✔
22

23
        // The buffer will encoded the invoice data using 5-bit groups (base32).
24
        var bufferBase32 bytes.Buffer
124✔
25

124✔
26
        // The timestamp will be encoded using 35 bits, in base32.
124✔
27
        timestampBase32 := uint64ToBase32(uint64(invoice.Timestamp.Unix()))
124✔
28

124✔
29
        // The timestamp must be exactly 35 bits, which means 7 groups. If it
124✔
30
        // can fit into fewer groups we add leading zero groups, if it is too
124✔
31
        // big we fail early, as there is not possible to encode it.
124✔
32
        if len(timestampBase32) > timestampBase32Len {
124✔
33
                return "", fmt.Errorf("timestamp too big: %d",
×
34
                        invoice.Timestamp.Unix())
×
35
        }
×
36

37
        // Add zero bytes to the first timestampBase32Len-len(timestampBase32)
38
        // groups, then add the non-zero groups.
39
        zeroes := make([]byte, timestampBase32Len-len(timestampBase32))
124✔
40
        _, err := bufferBase32.Write(zeroes)
124✔
41
        if err != nil {
124✔
42
                return "", fmt.Errorf("unable to write to buffer: %w", err)
×
43
        }
×
44
        _, err = bufferBase32.Write(timestampBase32)
124✔
45
        if err != nil {
124✔
46
                return "", fmt.Errorf("unable to write to buffer: %w", err)
×
47
        }
×
48

49
        // We now write the tagged fields to the buffer, which will fill the
50
        // rest of the data part before the signature.
51
        if err := writeTaggedFields(&bufferBase32, invoice); err != nil {
124✔
52
                return "", err
×
53
        }
×
54

55
        // The human-readable part (hrp) is "ln" + net hrp + optional amount,
56
        // except for signet where we add an additional "s" to differentiate it
57
        // from the older testnet3 (Core devs decided to use the same hrp for
58
        // signet as for testnet3 which is not optimal for LN). See
59
        // https://github.com/lightningnetwork/lightning-rfc/pull/844 for more
60
        // information.
61
        hrp := "ln" + invoice.Net.Bech32HRPSegwit
124✔
62
        if invoice.Net.Name == chaincfg.SigNetParams.Name {
124✔
UNCOV
63
                hrp = "lntbs"
×
UNCOV
64
        }
×
65
        if invoice.MilliSat != nil {
241✔
66
                // Encode the amount using the fewest possible characters.
117✔
67
                am, err := encodeAmount(*invoice.MilliSat)
117✔
68
                if err != nil {
117✔
69
                        return "", err
×
70
                }
×
71
                hrp += am
117✔
72
        }
73

74
        // The signature is over the single SHA-256 hash of the hrp + the
75
        // tagged fields encoded in base256.
76
        taggedFieldsBytes, err := bech32.ConvertBits(bufferBase32.Bytes(), 5, 8, true)
124✔
77
        if err != nil {
124✔
78
                return "", err
×
79
        }
×
80

81
        toSign := append([]byte(hrp), taggedFieldsBytes...)
124✔
82

124✔
83
        // We use compact signature format, and also encoded the recovery ID
124✔
84
        // such that a reader of the invoice can recover our pubkey from the
124✔
85
        // signature.
124✔
86
        sign, err := signer.SignCompact(toSign)
124✔
87
        if err != nil {
124✔
88
                return "", err
×
89
        }
×
90

91
        // From the header byte we can extract the recovery ID, and the last 64
92
        // bytes encode the signature.
93
        recoveryID := sign[0] - 27 - 4
124✔
94
        sig, err := lnwire.NewSigFromWireECDSA(sign[1:])
124✔
95
        if err != nil {
124✔
96
                return "", err
×
97
        }
×
98

99
        // If the pubkey field was explicitly set, it must be set to the pubkey
100
        // used to create the signature.
101
        if invoice.Destination != nil {
137✔
102
                signature, err := sig.ToSignature()
13✔
103
                if err != nil {
13✔
104
                        return "", fmt.Errorf("unable to deserialize "+
×
105
                                "signature: %v", err)
×
106
                }
×
107

108
                hash := chainhash.HashB(toSign)
13✔
109
                valid := signature.Verify(hash, invoice.Destination)
13✔
110
                if !valid {
18✔
111
                        return "", fmt.Errorf("signature does not match " +
5✔
112
                                "provided pubkey")
5✔
113
                }
5✔
114
        }
115

116
        // Convert the signature to base32 before writing it to the buffer.
117
        signBase32, err := bech32.ConvertBits(
119✔
118
                append(sig.RawBytes(), recoveryID),
119✔
119
                8, 5, true,
119✔
120
        )
119✔
121
        if err != nil {
119✔
122
                return "", err
×
123
        }
×
124
        bufferBase32.Write(signBase32)
119✔
125

119✔
126
        // Now we can create the bech32 encoded string from the base32 buffer.
119✔
127
        b32, err := bech32.Encode(hrp, bufferBase32.Bytes())
119✔
128
        if err != nil {
119✔
129
                return "", err
×
130
        }
×
131

132
        // Before returning, check that the bech32 encoded string is not greater
133
        // than our largest supported invoice size.
134
        if len(b32) > maxInvoiceLength {
119✔
135
                return "", ErrInvoiceTooLarge
×
136
        }
×
137

138
        return b32, nil
119✔
139
}
140

141
// writeTaggedFields writes the non-nil tagged fields of the Invoice to the
142
// base32 buffer.
143
func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error {
124✔
144
        if invoice.PaymentHash != nil {
248✔
145
                err := writeBytes32(bufferBase32, fieldTypeP, *invoice.PaymentHash)
124✔
146
                if err != nil {
124✔
147
                        return err
×
148
                }
×
149
        }
150

151
        if invoice.Description != nil {
240✔
152
                base32, err := bech32.ConvertBits([]byte(*invoice.Description),
116✔
153
                        8, 5, true)
116✔
154
                if err != nil {
116✔
155
                        return err
×
156
                }
×
157
                err = writeTaggedField(bufferBase32, fieldTypeD, base32)
116✔
158
                if err != nil {
116✔
159
                        return err
×
160
                }
×
161
        }
162

163
        if invoice.DescriptionHash != nil {
132✔
164
                err := writeBytes32(
8✔
165
                        bufferBase32, fieldTypeH, *invoice.DescriptionHash,
8✔
166
                )
8✔
167
                if err != nil {
8✔
168
                        return err
×
169
                }
×
170
        }
171

172
        if invoice.Metadata != nil {
130✔
173
                base32, err := bech32.ConvertBits(invoice.Metadata, 8, 5, true)
6✔
174
                if err != nil {
6✔
175
                        return err
×
176
                }
×
177
                err = writeTaggedField(bufferBase32, fieldTypeM, base32)
6✔
178
                if err != nil {
6✔
179
                        return err
×
180
                }
×
181
        }
182

183
        if invoice.minFinalCLTVExpiry != nil {
128✔
184
                finalDelta := uint64ToBase32(*invoice.minFinalCLTVExpiry)
4✔
185
                err := writeTaggedField(bufferBase32, fieldTypeC, finalDelta)
4✔
186
                if err != nil {
4✔
187
                        return err
×
188
                }
×
189
        }
190

191
        if invoice.expiry != nil {
215✔
192
                seconds := invoice.expiry.Seconds()
91✔
193
                expiry := uint64ToBase32(uint64(seconds))
91✔
194
                err := writeTaggedField(bufferBase32, fieldTypeX, expiry)
91✔
195
                if err != nil {
91✔
196
                        return err
×
197
                }
×
198
        }
199

200
        if invoice.FallbackAddr != nil {
131✔
201
                var version byte
7✔
202
                switch addr := invoice.FallbackAddr.(type) {
7✔
203
                case *btcutil.AddressPubKeyHash:
4✔
204
                        version = 17
4✔
205
                case *btcutil.AddressScriptHash:
1✔
206
                        version = 18
1✔
207
                case *btcutil.AddressWitnessPubKeyHash:
1✔
208
                        version = addr.WitnessVersion()
1✔
209
                case *btcutil.AddressWitnessScriptHash:
1✔
210
                        version = addr.WitnessVersion()
1✔
211
                default:
×
212
                        return fmt.Errorf("unknown fallback address type")
×
213
                }
214
                base32Addr, err := bech32.ConvertBits(
7✔
215
                        invoice.FallbackAddr.ScriptAddress(), 8, 5, true)
7✔
216
                if err != nil {
7✔
217
                        return err
×
218
                }
×
219

220
                err = writeTaggedField(bufferBase32, fieldTypeF,
7✔
221
                        append([]byte{version}, base32Addr...))
7✔
222
                if err != nil {
7✔
223
                        return err
×
224
                }
×
225
        }
226

227
        for _, routeHint := range invoice.RouteHints {
127✔
228
                // Each hop hint is encoded using 51 bytes, so we'll make to
3✔
229
                // sure to allocate enough space for the whole route hint.
3✔
230
                routeHintBase256 := make([]byte, 0, hopHintLen*len(routeHint))
3✔
231

3✔
232
                for _, hopHint := range routeHint {
8✔
233
                        hopHintBase256 := make([]byte, hopHintLen)
5✔
234
                        copy(hopHintBase256[:33], hopHint.NodeID.SerializeCompressed())
5✔
235
                        binary.BigEndian.PutUint64(
5✔
236
                                hopHintBase256[33:41], hopHint.ChannelID,
5✔
237
                        )
5✔
238
                        binary.BigEndian.PutUint32(
5✔
239
                                hopHintBase256[41:45], hopHint.FeeBaseMSat,
5✔
240
                        )
5✔
241
                        binary.BigEndian.PutUint32(
5✔
242
                                hopHintBase256[45:49], hopHint.FeeProportionalMillionths,
5✔
243
                        )
5✔
244
                        binary.BigEndian.PutUint16(
5✔
245
                                hopHintBase256[49:51], hopHint.CLTVExpiryDelta,
5✔
246
                        )
5✔
247
                        routeHintBase256 = append(routeHintBase256, hopHintBase256...)
5✔
248
                }
5✔
249

250
                routeHintBase32, err := bech32.ConvertBits(
3✔
251
                        routeHintBase256, 8, 5, true,
3✔
252
                )
3✔
253
                if err != nil {
3✔
254
                        return err
×
255
                }
×
256

257
                err = writeTaggedField(bufferBase32, fieldTypeR, routeHintBase32)
3✔
258
                if err != nil {
3✔
259
                        return err
×
260
                }
×
261
        }
262

263
        for _, path := range invoice.BlindedPaymentPaths {
128✔
264
                var buf bytes.Buffer
4✔
265

4✔
266
                err := path.Encode(&buf)
4✔
267
                if err != nil {
4✔
268
                        return err
×
269
                }
×
270

271
                blindedPathBase32, err := bech32.ConvertBits(
4✔
272
                        buf.Bytes(), 8, 5, true,
4✔
273
                )
4✔
274
                if err != nil {
4✔
275
                        return err
×
276
                }
×
277

278
                err = writeTaggedField(
4✔
279
                        bufferBase32, fieldTypeB, blindedPathBase32,
4✔
280
                )
4✔
281
                if err != nil {
4✔
282
                        return err
×
283
                }
×
284
        }
285

286
        if invoice.Destination != nil {
137✔
287
                // Convert 33 byte pubkey to 53 5-bit groups.
13✔
288
                pubKeyBase32, err := bech32.ConvertBits(
13✔
289
                        invoice.Destination.SerializeCompressed(), 8, 5, true)
13✔
290
                if err != nil {
13✔
291
                        return err
×
292
                }
×
293

294
                if len(pubKeyBase32) != pubKeyBase32Len {
13✔
295
                        return fmt.Errorf("invalid pubkey length: %d",
×
296
                                len(invoice.Destination.SerializeCompressed()))
×
297
                }
×
298

299
                err = writeTaggedField(bufferBase32, fieldTypeN, pubKeyBase32)
13✔
300
                if err != nil {
13✔
301
                        return err
×
302
                }
×
303
        }
304

305
        if invoice.PaymentAddr != nil {
248✔
306
                err := writeBytes32(
124✔
307
                        bufferBase32, fieldTypeS, *invoice.PaymentAddr,
124✔
308
                )
124✔
309
                if err != nil {
124✔
NEW
310
                        return err
×
NEW
311
                }
×
312
        }
313

314
        if invoice.Features.SerializeSize32() > 0 {
134✔
315
                var b bytes.Buffer
10✔
316
                err := invoice.Features.RawFeatureVector.EncodeBase32(&b)
10✔
317
                if err != nil {
10✔
318
                        return err
×
319
                }
×
320

321
                err = writeTaggedField(bufferBase32, fieldType9, b.Bytes())
10✔
322
                if err != nil {
10✔
323
                        return err
×
324
                }
×
325
        }
326

327
        return nil
124✔
328
}
329

330
// writeBytes32 encodes a 32-byte array as base32 and writes it to bufferBase32
331
// under the passed fieldType.
332
func writeBytes32(bufferBase32 *bytes.Buffer, fieldType byte, b [32]byte) error {
256✔
333
        // Convert 32 byte hash to 52 5-bit groups.
256✔
334
        base32, err := bech32.ConvertBits(b[:], 8, 5, true)
256✔
335
        if err != nil {
256✔
336
                return err
×
337
        }
×
338

339
        return writeTaggedField(bufferBase32, fieldType, base32)
256✔
340
}
341

342
// writeTaggedField takes the type of a tagged data field, and the data of
343
// the tagged field (encoded in base32), and writes the type, length and data
344
// to the buffer.
345
func writeTaggedField(bufferBase32 *bytes.Buffer, dataType byte, data []byte) error {
510✔
346
        // Length must be exactly 10 bits, so add leading zero groups if
510✔
347
        // needed.
510✔
348
        lenBase32 := uint64ToBase32(uint64(len(data)))
510✔
349
        for len(lenBase32) < 2 {
729✔
350
                lenBase32 = append([]byte{0}, lenBase32...)
219✔
351
        }
219✔
352

353
        if len(lenBase32) != 2 {
510✔
354
                return fmt.Errorf("data length too big to fit within 10 bits: %d",
×
355
                        len(data))
×
356
        }
×
357

358
        err := bufferBase32.WriteByte(dataType)
510✔
359
        if err != nil {
510✔
360
                return fmt.Errorf("unable to write to buffer: %w", err)
×
361
        }
×
362
        _, err = bufferBase32.Write(lenBase32)
510✔
363
        if err != nil {
510✔
364
                return fmt.Errorf("unable to write to buffer: %w", err)
×
365
        }
×
366
        _, err = bufferBase32.Write(data)
510✔
367
        if err != nil {
510✔
368
                return fmt.Errorf("unable to write to buffer: %w", err)
×
369
        }
×
370

371
        return nil
510✔
372
}
373

374
// uint64ToBase32 converts a uint64 to a base32 encoded integer encoded using
375
// as few 5-bit groups as possible.
376
func uint64ToBase32(num uint64) []byte {
730✔
377
        // Return at least one group.
730✔
378
        if num == 0 {
735✔
379
                return []byte{0}
5✔
380
        }
5✔
381

382
        // To fit an uint64, we need at most is ceil(64 / 5) = 13 groups.
383
        arr := make([]byte, 13)
725✔
384
        i := 13
725✔
385
        for num > 0 {
2,672✔
386
                i--
1,947✔
387
                arr[i] = byte(num & uint64(31)) // 0b11111 in binary
1,947✔
388
                num >>= 5
1,947✔
389
        }
1,947✔
390

391
        // We only return non-zero leading groups.
392
        return arr[i:]
725✔
393
}
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

© 2026 Coveralls, Inc