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

lightningnetwork / lnd / 10395438389

14 Aug 2024 09:42PM UTC coverage: 50.246% (-8.5%) from 58.725%
10395438389

Pull #9011

github

ziggie1984
multi: Add restriction of the ChanUpdate timestamp.

ChanUpdate timestamps are now restircted so that they cannot be
more than two weeks into the future. Moreover channels with both
timestamps in the ReplyChannelRange msg either too far in the past
or too far in the future are not queried.
Pull Request #9011: Fix TimeStamp issue in the Gossip Syncer

29 of 45 new or added lines in 3 files covered. (64.44%)

23069 existing lines in 396 files now uncovered.

96101 of 191260 relevant lines covered (50.25%)

2.08 hits per line

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

55.99
/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) {
4✔
18
        // First check that this invoice is valid before starting the encoding.
4✔
19
        if err := validateInvoice(invoice); err != nil {
4✔
UNCOV
20
                return "", err
×
UNCOV
21
        }
×
22

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

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

4✔
29
        // The timestamp must be exactly 35 bits, which means 7 groups. If it
4✔
30
        // can fit into fewer groups we add leading zero groups, if it is too
4✔
31
        // big we fail early, as there is not possible to encode it.
4✔
32
        if len(timestampBase32) > timestampBase32Len {
4✔
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))
4✔
40
        _, err := bufferBase32.Write(zeroes)
4✔
41
        if err != nil {
4✔
42
                return "", fmt.Errorf("unable to write to buffer: %w", err)
×
43
        }
×
44
        _, err = bufferBase32.Write(timestampBase32)
4✔
45
        if err != nil {
4✔
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 {
4✔
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
4✔
62
        if invoice.Net.Name == chaincfg.SigNetParams.Name {
4✔
UNCOV
63
                hrp = "lntbs"
×
UNCOV
64
        }
×
65
        if invoice.MilliSat != nil {
8✔
66
                // Encode the amount using the fewest possible characters.
4✔
67
                am, err := encodeAmount(*invoice.MilliSat)
4✔
68
                if err != nil {
4✔
69
                        return "", err
×
70
                }
×
71
                hrp += am
4✔
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)
4✔
77
        if err != nil {
4✔
78
                return "", err
×
79
        }
×
80

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

4✔
83
        // We use compact signature format, and also encoded the recovery ID
4✔
84
        // such that a reader of the invoice can recover our pubkey from the
4✔
85
        // signature.
4✔
86
        sign, err := signer.SignCompact(toSign)
4✔
87
        if err != nil {
4✔
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
4✔
94
        sig, err := lnwire.NewSigFromWireECDSA(sign[1:])
4✔
95
        if err != nil {
4✔
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 {
4✔
UNCOV
102
                signature, err := sig.ToSignature()
×
UNCOV
103
                if err != nil {
×
104
                        return "", fmt.Errorf("unable to deserialize "+
×
105
                                "signature: %v", err)
×
106
                }
×
107

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

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

4✔
126
        // Now we can create the bech32 encoded string from the base32 buffer.
4✔
127
        b32, err := bech32.Encode(hrp, bufferBase32.Bytes())
4✔
128
        if err != nil {
4✔
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 {
4✔
135
                return "", ErrInvoiceTooLarge
×
136
        }
×
137

138
        return b32, nil
4✔
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 {
4✔
144
        if invoice.PaymentHash != nil {
8✔
145
                err := writeBytes32(bufferBase32, fieldTypeP, *invoice.PaymentHash)
4✔
146
                if err != nil {
4✔
147
                        return err
×
148
                }
×
149
        }
150

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

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

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

183
        if invoice.minFinalCLTVExpiry != nil {
8✔
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 {
8✔
192
                seconds := invoice.expiry.Seconds()
4✔
193
                expiry := uint64ToBase32(uint64(seconds))
4✔
194
                err := writeTaggedField(bufferBase32, fieldTypeX, expiry)
4✔
195
                if err != nil {
4✔
196
                        return err
×
197
                }
×
198
        }
199

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

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

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

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

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

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

263
        for _, path := range invoice.BlindedPaymentPaths {
8✔
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 {
4✔
UNCOV
287
                // Convert 33 byte pubkey to 53 5-bit groups.
×
UNCOV
288
                pubKeyBase32, err := bech32.ConvertBits(
×
UNCOV
289
                        invoice.Destination.SerializeCompressed(), 8, 5, true)
×
UNCOV
290
                if err != nil {
×
291
                        return err
×
292
                }
×
293

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

UNCOV
299
                err = writeTaggedField(bufferBase32, fieldTypeN, pubKeyBase32)
×
UNCOV
300
                if err != nil {
×
301
                        return err
×
302
                }
×
303
        }
304
        if invoice.PaymentAddr != nil {
8✔
305
                err := writeBytes32(
4✔
306
                        bufferBase32, fieldTypeS, *invoice.PaymentAddr,
4✔
307
                )
4✔
308
                if err != nil {
4✔
309
                        return err
×
310
                }
×
311
        }
312
        if invoice.Features.SerializeSize32() > 0 {
8✔
313
                var b bytes.Buffer
4✔
314
                err := invoice.Features.RawFeatureVector.EncodeBase32(&b)
4✔
315
                if err != nil {
4✔
316
                        return err
×
317
                }
×
318

319
                err = writeTaggedField(bufferBase32, fieldType9, b.Bytes())
4✔
320
                if err != nil {
4✔
321
                        return err
×
322
                }
×
323
        }
324

325
        return nil
4✔
326
}
327

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

337
        return writeTaggedField(bufferBase32, fieldType, base32)
4✔
338
}
339

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

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

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

369
        return nil
4✔
370
}
371

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

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

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