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

lightningnetwork / lnd / 11170835610

03 Oct 2024 10:41PM UTC coverage: 49.188% (-9.6%) from 58.738%
11170835610

push

github

web-flow
Merge pull request #9154 from ziggie1984/master

multi: bump btcd version.

3 of 6 new or added lines in 6 files covered. (50.0%)

26110 existing lines in 428 files now uncovered.

97359 of 197934 relevant lines covered (49.19%)

1.04 hits per line

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

83.08
/record/blinded_data.go
1
package record
2

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

8
        "github.com/btcsuite/btcd/btcec/v2"
9
        "github.com/lightningnetwork/lnd/lnwire"
10
        "github.com/lightningnetwork/lnd/tlv"
11
)
12

13
// AverageDummyHopPayloadSize is the size of a standard blinded path dummy hop
14
// payload. In most cases, this is larger than the other payload types and so
15
// to make sure that a sender cannot use this fact to know if a dummy hop is
16
// present or not, we'll make sure to always pad all payloads to at least this
17
// size.
18
const AverageDummyHopPayloadSize = 51
19

20
// BlindedRouteData contains the information that is included in a blinded
21
// route encrypted data blob that is created by the recipient to provide
22
// forwarding information.
23
type BlindedRouteData struct {
24
        // Padding is an optional set of bytes that a recipient can use to pad
25
        // the data so that the encrypted recipient data blobs are all the same
26
        // length.
27
        Padding tlv.OptionalRecordT[tlv.TlvType1, []byte]
28

29
        // ShortChannelID is the channel ID of the next hop.
30
        ShortChannelID tlv.OptionalRecordT[tlv.TlvType2, lnwire.ShortChannelID]
31

32
        // NextNodeID is the node ID of the next node on the path. In the
33
        // context of blinded path payments, this is used to indicate the
34
        // presence of dummy hops that need to be peeled from the onion.
35
        NextNodeID tlv.OptionalRecordT[tlv.TlvType4, *btcec.PublicKey]
36

37
        // PathID is a secret set of bytes that the blinded path creator will
38
        // set so that they can check the value on decryption to ensure that the
39
        // path they created was used for the intended purpose.
40
        PathID tlv.OptionalRecordT[tlv.TlvType6, []byte]
41

42
        // NextBlindingOverride is a blinding point that should be switched
43
        // in for the next hop. This is used to combine two blinded paths into
44
        // one (which primarily is used in onion messaging, but in theory
45
        // could be used for payments as well).
46
        NextBlindingOverride tlv.OptionalRecordT[tlv.TlvType8, *btcec.PublicKey]
47

48
        // RelayInfo provides the relay parameters for the hop.
49
        RelayInfo tlv.OptionalRecordT[tlv.TlvType10, PaymentRelayInfo]
50

51
        // Constraints provides the payment relay constraints for the hop.
52
        Constraints tlv.OptionalRecordT[tlv.TlvType12, PaymentConstraints]
53

54
        // Features is the set of features the payment requires.
55
        Features tlv.OptionalRecordT[tlv.TlvType14, lnwire.FeatureVector]
56
}
57

58
// NewNonFinalBlindedRouteData creates the data that's provided for hops within
59
// a blinded route.
60
func NewNonFinalBlindedRouteData(chanID lnwire.ShortChannelID,
61
        blindingOverride *btcec.PublicKey, relayInfo PaymentRelayInfo,
62
        constraints *PaymentConstraints,
63
        features *lnwire.FeatureVector) *BlindedRouteData {
2✔
64

2✔
65
        info := &BlindedRouteData{
2✔
66
                ShortChannelID: tlv.SomeRecordT(
2✔
67
                        tlv.NewRecordT[tlv.TlvType2](chanID),
2✔
68
                ),
2✔
69
                RelayInfo: tlv.SomeRecordT(
2✔
70
                        tlv.NewRecordT[tlv.TlvType10](relayInfo),
2✔
71
                ),
2✔
72
        }
2✔
73

2✔
74
        if blindingOverride != nil {
2✔
UNCOV
75
                info.NextBlindingOverride = tlv.SomeRecordT(
×
UNCOV
76
                        tlv.NewPrimitiveRecord[tlv.TlvType8](blindingOverride))
×
UNCOV
77
        }
×
78

79
        if constraints != nil {
4✔
80
                info.Constraints = tlv.SomeRecordT(
2✔
81
                        tlv.NewRecordT[tlv.TlvType12](*constraints))
2✔
82
        }
2✔
83

84
        if features != nil {
2✔
UNCOV
85
                info.Features = tlv.SomeRecordT(
×
UNCOV
86
                        tlv.NewRecordT[tlv.TlvType14](*features),
×
UNCOV
87
                )
×
UNCOV
88
        }
×
89

90
        return info
2✔
91
}
92

93
// NewFinalHopBlindedRouteData creates the data that's provided for the final
94
// hop in a blinded route.
95
func NewFinalHopBlindedRouteData(constraints *PaymentConstraints,
96
        pathID []byte) *BlindedRouteData {
2✔
97

2✔
98
        var data BlindedRouteData
2✔
99
        if pathID != nil {
4✔
100
                data.PathID = tlv.SomeRecordT(
2✔
101
                        tlv.NewPrimitiveRecord[tlv.TlvType6](pathID),
2✔
102
                )
2✔
103
        }
2✔
104

105
        if constraints != nil {
4✔
106
                data.Constraints = tlv.SomeRecordT(
2✔
107
                        tlv.NewRecordT[tlv.TlvType12](*constraints))
2✔
108
        }
2✔
109

110
        return &data
2✔
111
}
112

113
// NewDummyHopRouteData creates the data that's provided for any hop preceding
114
// a dummy hop. The presence of such a payload indicates to the reader that
115
// they are the intended recipient and should peel the remainder of the onion.
116
func NewDummyHopRouteData(ourPubKey *btcec.PublicKey,
117
        relayInfo PaymentRelayInfo,
118
        constraints PaymentConstraints) *BlindedRouteData {
2✔
119

2✔
120
        return &BlindedRouteData{
2✔
121
                NextNodeID: tlv.SomeRecordT(
2✔
122
                        tlv.NewPrimitiveRecord[tlv.TlvType4](ourPubKey),
2✔
123
                ),
2✔
124
                RelayInfo: tlv.SomeRecordT(
2✔
125
                        tlv.NewRecordT[tlv.TlvType10](relayInfo),
2✔
126
                ),
2✔
127
                Constraints: tlv.SomeRecordT(
2✔
128
                        tlv.NewRecordT[tlv.TlvType12](constraints),
2✔
129
                ),
2✔
130
        }
2✔
131
}
2✔
132

133
// DecodeBlindedRouteData decodes the data provided within a blinded route.
134
func DecodeBlindedRouteData(r io.Reader) (*BlindedRouteData, error) {
2✔
135
        var (
2✔
136
                d BlindedRouteData
2✔
137

2✔
138
                padding          = d.Padding.Zero()
2✔
139
                scid             = d.ShortChannelID.Zero()
2✔
140
                nextNodeID       = d.NextNodeID.Zero()
2✔
141
                pathID           = d.PathID.Zero()
2✔
142
                blindingOverride = d.NextBlindingOverride.Zero()
2✔
143
                relayInfo        = d.RelayInfo.Zero()
2✔
144
                constraints      = d.Constraints.Zero()
2✔
145
                features         = d.Features.Zero()
2✔
146
        )
2✔
147

2✔
148
        var tlvRecords lnwire.ExtraOpaqueData
2✔
149
        if err := lnwire.ReadElements(r, &tlvRecords); err != nil {
2✔
150
                return nil, err
×
151
        }
×
152

153
        typeMap, err := tlvRecords.ExtractRecords(
2✔
154
                &padding, &scid, &nextNodeID, &pathID, &blindingOverride,
2✔
155
                &relayInfo, &constraints, &features,
2✔
156
        )
2✔
157
        if err != nil {
2✔
UNCOV
158
                return nil, err
×
UNCOV
159
        }
×
160

161
        val, ok := typeMap[d.Padding.TlvType()]
2✔
162
        if ok && val == nil {
4✔
163
                d.Padding = tlv.SomeRecordT(padding)
2✔
164
        }
2✔
165

166
        if val, ok := typeMap[d.ShortChannelID.TlvType()]; ok && val == nil {
4✔
167
                d.ShortChannelID = tlv.SomeRecordT(scid)
2✔
168
        }
2✔
169

170
        if val, ok := typeMap[d.NextNodeID.TlvType()]; ok && val == nil {
4✔
171
                d.NextNodeID = tlv.SomeRecordT(nextNodeID)
2✔
172
        }
2✔
173

174
        if val, ok := typeMap[d.PathID.TlvType()]; ok && val == nil {
4✔
175
                d.PathID = tlv.SomeRecordT(pathID)
2✔
176
        }
2✔
177

178
        val, ok = typeMap[d.NextBlindingOverride.TlvType()]
2✔
179
        if ok && val == nil {
2✔
UNCOV
180
                d.NextBlindingOverride = tlv.SomeRecordT(blindingOverride)
×
UNCOV
181
        }
×
182

183
        if val, ok := typeMap[d.RelayInfo.TlvType()]; ok && val == nil {
4✔
184
                d.RelayInfo = tlv.SomeRecordT(relayInfo)
2✔
185
        }
2✔
186

187
        if val, ok := typeMap[d.Constraints.TlvType()]; ok && val == nil {
4✔
188
                d.Constraints = tlv.SomeRecordT(constraints)
2✔
189
        }
2✔
190

191
        if val, ok := typeMap[d.Features.TlvType()]; ok && val == nil {
2✔
UNCOV
192
                d.Features = tlv.SomeRecordT(features)
×
UNCOV
193
        }
×
194

195
        return &d, nil
2✔
196
}
197

198
// EncodeBlindedRouteData encodes the blinded route data provided.
199
func EncodeBlindedRouteData(data *BlindedRouteData) ([]byte, error) {
2✔
200
        var (
2✔
201
                e               lnwire.ExtraOpaqueData
2✔
202
                recordProducers = make([]tlv.RecordProducer, 0, 5)
2✔
203
        )
2✔
204

2✔
205
        data.Padding.WhenSome(func(p tlv.RecordT[tlv.TlvType1, []byte]) {
4✔
206
                recordProducers = append(recordProducers, &p)
2✔
207
        })
2✔
208

209
        data.ShortChannelID.WhenSome(func(scid tlv.RecordT[tlv.TlvType2,
2✔
210
                lnwire.ShortChannelID]) {
4✔
211

2✔
212
                recordProducers = append(recordProducers, &scid)
2✔
213
        })
2✔
214

215
        data.NextNodeID.WhenSome(func(f tlv.RecordT[tlv.TlvType4,
2✔
216
                *btcec.PublicKey]) {
4✔
217

2✔
218
                recordProducers = append(recordProducers, &f)
2✔
219
        })
2✔
220

221
        data.PathID.WhenSome(func(pathID tlv.RecordT[tlv.TlvType6, []byte]) {
4✔
222
                recordProducers = append(recordProducers, &pathID)
2✔
223
        })
2✔
224

225
        data.NextBlindingOverride.WhenSome(func(pk tlv.RecordT[tlv.TlvType8,
2✔
226
                *btcec.PublicKey]) {
2✔
UNCOV
227

×
UNCOV
228
                recordProducers = append(recordProducers, &pk)
×
UNCOV
229
        })
×
230

231
        data.RelayInfo.WhenSome(func(r tlv.RecordT[tlv.TlvType10,
2✔
232
                PaymentRelayInfo]) {
4✔
233

2✔
234
                recordProducers = append(recordProducers, &r)
2✔
235
        })
2✔
236

237
        data.Constraints.WhenSome(func(cs tlv.RecordT[tlv.TlvType12,
2✔
238
                PaymentConstraints]) {
4✔
239

2✔
240
                recordProducers = append(recordProducers, &cs)
2✔
241
        })
2✔
242

243
        data.Features.WhenSome(func(f tlv.RecordT[tlv.TlvType14,
2✔
244
                lnwire.FeatureVector]) {
2✔
UNCOV
245

×
UNCOV
246
                recordProducers = append(recordProducers, &f)
×
UNCOV
247
        })
×
248

249
        if err := e.PackRecords(recordProducers...); err != nil {
2✔
250
                return nil, err
×
251
        }
×
252

253
        return e[:], nil
2✔
254
}
255

256
// PadBy adds "n" padding bytes to the BlindedRouteData using the Padding field.
257
// Callers should be aware that the total payload size will change by more than
258
// "n" since the "n" bytes will be prefixed by BigSize type and length fields.
259
// Callers may need to call PadBy iteratively until each encrypted data packet
260
// is the same size and so each call will overwrite the Padding record.
261
// Note that calling PadBy with an n value of 0 will still result in a zero
262
// length TLV entry being added.
263
func (b *BlindedRouteData) PadBy(n int) {
2✔
264
        b.Padding = tlv.SomeRecordT(
2✔
265
                tlv.NewPrimitiveRecord[tlv.TlvType1](make([]byte, n)),
2✔
266
        )
2✔
267
}
2✔
268

269
// PaymentRelayInfo describes the relay policy for a blinded path.
270
type PaymentRelayInfo struct {
271
        // CltvExpiryDelta is the expiry delta for the payment.
272
        CltvExpiryDelta uint16
273

274
        // FeeRate is the fee rate that will be charged per millionth of a
275
        // satoshi.
276
        FeeRate uint32
277

278
        // BaseFee is the per-htlc fee charged in milli-satoshis.
279
        BaseFee lnwire.MilliSatoshi
280
}
281

282
// Record creates a tlv.Record that encodes the payment relay (type 10) type for
283
// an encrypted blob payload.
284
func (i *PaymentRelayInfo) Record() tlv.Record {
2✔
285
        return tlv.MakeDynamicRecord(
2✔
286
                10, &i, func() uint64 {
4✔
287
                        // uint16 + uint32 + tuint32
2✔
288
                        return 2 + 4 + tlv.SizeTUint32(uint32(i.BaseFee))
2✔
289
                }, encodePaymentRelay, decodePaymentRelay,
2✔
290
        )
291
}
292

293
func encodePaymentRelay(w io.Writer, val interface{}, buf *[8]byte) error {
2✔
294
        if t, ok := val.(**PaymentRelayInfo); ok {
4✔
295
                relayInfo := *t
2✔
296

2✔
297
                // Just write our first 6 bytes directly.
2✔
298
                binary.BigEndian.PutUint16(buf[:2], relayInfo.CltvExpiryDelta)
2✔
299
                binary.BigEndian.PutUint32(buf[2:6], relayInfo.FeeRate)
2✔
300
                if _, err := w.Write(buf[0:6]); err != nil {
2✔
301
                        return err
×
302
                }
×
303

304
                baseFee := uint32(relayInfo.BaseFee)
2✔
305

2✔
306
                // We can safely reuse buf here because we overwrite its
2✔
307
                // contents.
2✔
308
                return tlv.ETUint32(w, &baseFee, buf)
2✔
309
        }
310

311
        return tlv.NewTypeForEncodingErr(val, "**hop.PaymentRelayInfo")
×
312
}
313

314
func decodePaymentRelay(r io.Reader, val interface{}, buf *[8]byte,
315
        l uint64) error {
2✔
316

2✔
317
        if t, ok := val.(**PaymentRelayInfo); ok && l <= 10 {
4✔
318
                scratch := make([]byte, l)
2✔
319

2✔
320
                n, err := io.ReadFull(r, scratch)
2✔
321
                if err != nil {
2✔
322
                        return err
×
323
                }
×
324

325
                // We expect at least 6 bytes, because we have 2 bytes for
326
                // cltv delta and 4 bytes for fee rate.
327
                if n < 6 {
2✔
328
                        return tlv.NewTypeForDecodingErr(val,
×
329
                                "*hop.paymentRelayInfo", uint64(n), 6)
×
330
                }
×
331

332
                relayInfo := *t
2✔
333

2✔
334
                relayInfo.CltvExpiryDelta = binary.BigEndian.Uint16(
2✔
335
                        scratch[0:2],
2✔
336
                )
2✔
337
                relayInfo.FeeRate = binary.BigEndian.Uint32(scratch[2:6])
2✔
338

2✔
339
                // To be able to re-use the DTUint32 function we create a
2✔
340
                // buffer with just the bytes holding the variable length u32.
2✔
341
                // If the base fee is zero, this will be an empty buffer, which
2✔
342
                // is okay.
2✔
343
                b := bytes.NewBuffer(scratch[6:])
2✔
344

2✔
345
                var baseFee uint32
2✔
346
                err = tlv.DTUint32(b, &baseFee, buf, l-6)
2✔
347
                if err != nil {
2✔
348
                        return err
×
349
                }
×
350

351
                relayInfo.BaseFee = lnwire.MilliSatoshi(baseFee)
2✔
352

2✔
353
                return nil
2✔
354
        }
355

356
        return tlv.NewTypeForDecodingErr(val, "*hop.paymentRelayInfo", l, 10)
×
357
}
358

359
// PaymentConstraints is a set of restrictions on a payment.
360
type PaymentConstraints struct {
361
        // MaxCltvExpiry is the maximum expiry height for the payment.
362
        MaxCltvExpiry uint32
363

364
        // HtlcMinimumMsat is the minimum htlc size for the payment.
365
        HtlcMinimumMsat lnwire.MilliSatoshi
366
}
367

368
func (p *PaymentConstraints) Record() tlv.Record {
2✔
369
        return tlv.MakeDynamicRecord(
2✔
370
                12, &p, func() uint64 {
4✔
371
                        // uint32 + tuint64.
2✔
372
                        return 4 + tlv.SizeTUint64(uint64(
2✔
373
                                p.HtlcMinimumMsat,
2✔
374
                        ))
2✔
375
                },
2✔
376
                encodePaymentConstraints, decodePaymentConstraints,
377
        )
378
}
379

380
func encodePaymentConstraints(w io.Writer, val interface{},
381
        buf *[8]byte) error {
2✔
382

2✔
383
        if c, ok := val.(**PaymentConstraints); ok {
4✔
384
                constraints := *c
2✔
385

2✔
386
                binary.BigEndian.PutUint32(buf[:4], constraints.MaxCltvExpiry)
2✔
387
                if _, err := w.Write(buf[:4]); err != nil {
2✔
388
                        return err
×
389
                }
×
390

391
                // We can safely re-use buf here because we overwrite its
392
                // contents.
393
                htlcMsat := uint64(constraints.HtlcMinimumMsat)
2✔
394

2✔
395
                return tlv.ETUint64(w, &htlcMsat, buf)
2✔
396
        }
397

398
        return tlv.NewTypeForEncodingErr(val, "**PaymentConstraints")
×
399
}
400

401
func decodePaymentConstraints(r io.Reader, val interface{}, buf *[8]byte,
402
        l uint64) error {
2✔
403

2✔
404
        if c, ok := val.(**PaymentConstraints); ok && l <= 12 {
4✔
405
                scratch := make([]byte, l)
2✔
406

2✔
407
                n, err := io.ReadFull(r, scratch)
2✔
408
                if err != nil {
2✔
409
                        return err
×
410
                }
×
411

412
                // We expect at least 4 bytes for our uint32.
413
                if n < 4 {
2✔
414
                        return tlv.NewTypeForDecodingErr(val,
×
415
                                "*paymentConstraints", uint64(n), 4)
×
416
                }
×
417

418
                payConstraints := *c
2✔
419

2✔
420
                payConstraints.MaxCltvExpiry = binary.BigEndian.Uint32(
2✔
421
                        scratch[:4],
2✔
422
                )
2✔
423

2✔
424
                // This could be empty if our minimum is zero, that's okay.
2✔
425
                var (
2✔
426
                        b       = bytes.NewBuffer(scratch[4:])
2✔
427
                        minHtlc uint64
2✔
428
                )
2✔
429

2✔
430
                err = tlv.DTUint64(b, &minHtlc, buf, l-4)
2✔
431
                if err != nil {
2✔
432
                        return err
×
433
                }
×
434
                payConstraints.HtlcMinimumMsat = lnwire.MilliSatoshi(minHtlc)
2✔
435

2✔
436
                return nil
2✔
437
        }
438

439
        return tlv.NewTypeForDecodingErr(val, "**PaymentConstraints", l, l)
×
440
}
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