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

lightningnetwork / lnd / 9915780197

13 Jul 2024 12:30AM UTC coverage: 49.268% (-9.1%) from 58.413%
9915780197

push

github

web-flow
Merge pull request #8653 from ProofOfKeags/fn-prim

DynComms [0/n]: `fn` package additions

92837 of 188433 relevant lines covered (49.27%)

1.55 hits per line

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

38.36
/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
// BlindedRouteData contains the information that is included in a blinded
14
// route encrypted data blob that is created by the recipient to provide
15
// forwarding information.
16
type BlindedRouteData struct {
17
        // Padding is an optional set of bytes that a recipient can use to pad
18
        // the data so that the encrypted recipient data blobs are all the same
19
        // length.
20
        Padding tlv.OptionalRecordT[tlv.TlvType1, []byte]
21

22
        // ShortChannelID is the channel ID of the next hop.
23
        ShortChannelID tlv.OptionalRecordT[tlv.TlvType2, lnwire.ShortChannelID]
24

25
        // PathID is a secret set of bytes that the blinded path creator will
26
        // set so that they can check the value on decryption to ensure that the
27
        // path they created was used for the intended purpose.
28
        PathID tlv.OptionalRecordT[tlv.TlvType6, []byte]
29

30
        // NextBlindingOverride is a blinding point that should be switched
31
        // in for the next hop. This is used to combine two blinded paths into
32
        // one (which primarily is used in onion messaging, but in theory
33
        // could be used for payments as well).
34
        NextBlindingOverride tlv.OptionalRecordT[tlv.TlvType8, *btcec.PublicKey]
35

36
        // RelayInfo provides the relay parameters for the hop.
37
        RelayInfo tlv.OptionalRecordT[tlv.TlvType10, PaymentRelayInfo]
38

39
        // Constraints provides the payment relay constraints for the hop.
40
        Constraints tlv.OptionalRecordT[tlv.TlvType12, PaymentConstraints]
41

42
        // Features is the set of features the payment requires.
43
        Features tlv.OptionalRecordT[tlv.TlvType14, lnwire.FeatureVector]
44
}
45

46
// NewNonFinalBlindedRouteData creates the data that's provided for hops within
47
// a blinded route.
48
func NewNonFinalBlindedRouteData(chanID lnwire.ShortChannelID,
49
        blindingOverride *btcec.PublicKey, relayInfo PaymentRelayInfo,
50
        constraints *PaymentConstraints,
51
        features *lnwire.FeatureVector) *BlindedRouteData {
×
52

×
53
        info := &BlindedRouteData{
×
54
                ShortChannelID: tlv.SomeRecordT(
×
55
                        tlv.NewRecordT[tlv.TlvType2](chanID),
×
56
                ),
×
57
                RelayInfo: tlv.SomeRecordT(
×
58
                        tlv.NewRecordT[tlv.TlvType10](relayInfo),
×
59
                ),
×
60
        }
×
61

×
62
        if blindingOverride != nil {
×
63
                info.NextBlindingOverride = tlv.SomeRecordT(
×
64
                        tlv.NewPrimitiveRecord[tlv.TlvType8](blindingOverride))
×
65
        }
×
66

67
        if constraints != nil {
×
68
                info.Constraints = tlv.SomeRecordT(
×
69
                        tlv.NewRecordT[tlv.TlvType12](*constraints))
×
70
        }
×
71

72
        if features != nil {
×
73
                info.Features = tlv.SomeRecordT(
×
74
                        tlv.NewRecordT[tlv.TlvType14](*features),
×
75
                )
×
76
        }
×
77

78
        return info
×
79
}
80

81
// NewFinalHopBlindedRouteData creates the data that's provided for the final
82
// hop in a blinded route.
83
func NewFinalHopBlindedRouteData(constraints *PaymentConstraints,
84
        pathID []byte) *BlindedRouteData {
×
85

×
86
        var data BlindedRouteData
×
87
        if pathID != nil {
×
88
                data.PathID = tlv.SomeRecordT(
×
89
                        tlv.NewPrimitiveRecord[tlv.TlvType6](pathID),
×
90
                )
×
91
        }
×
92

93
        if constraints != nil {
×
94
                data.Constraints = tlv.SomeRecordT(
×
95
                        tlv.NewRecordT[tlv.TlvType12](*constraints))
×
96
        }
×
97

98
        return &data
×
99
}
100

101
// DecodeBlindedRouteData decodes the data provided within a blinded route.
102
func DecodeBlindedRouteData(r io.Reader) (*BlindedRouteData, error) {
3✔
103
        var (
3✔
104
                d BlindedRouteData
3✔
105

3✔
106
                padding          = d.Padding.Zero()
3✔
107
                scid             = d.ShortChannelID.Zero()
3✔
108
                pathID           = d.PathID.Zero()
3✔
109
                blindingOverride = d.NextBlindingOverride.Zero()
3✔
110
                relayInfo        = d.RelayInfo.Zero()
3✔
111
                constraints      = d.Constraints.Zero()
3✔
112
                features         = d.Features.Zero()
3✔
113
        )
3✔
114

3✔
115
        var tlvRecords lnwire.ExtraOpaqueData
3✔
116
        if err := lnwire.ReadElements(r, &tlvRecords); err != nil {
3✔
117
                return nil, err
×
118
        }
×
119

120
        typeMap, err := tlvRecords.ExtractRecords(
3✔
121
                &padding, &scid, &pathID, &blindingOverride, &relayInfo,
3✔
122
                &constraints, &features,
3✔
123
        )
3✔
124
        if err != nil {
3✔
125
                return nil, err
×
126
        }
×
127

128
        val, ok := typeMap[d.Padding.TlvType()]
3✔
129
        if ok && val == nil {
3✔
130
                d.Padding = tlv.SomeRecordT(padding)
×
131
        }
×
132

133
        if val, ok := typeMap[d.ShortChannelID.TlvType()]; ok && val == nil {
6✔
134
                d.ShortChannelID = tlv.SomeRecordT(scid)
3✔
135
        }
3✔
136

137
        if val, ok := typeMap[d.PathID.TlvType()]; ok && val == nil {
3✔
138
                d.PathID = tlv.SomeRecordT(pathID)
×
139
        }
×
140

141
        val, ok = typeMap[d.NextBlindingOverride.TlvType()]
3✔
142
        if ok && val == nil {
3✔
143
                d.NextBlindingOverride = tlv.SomeRecordT(blindingOverride)
×
144
        }
×
145

146
        if val, ok := typeMap[d.RelayInfo.TlvType()]; ok && val == nil {
6✔
147
                d.RelayInfo = tlv.SomeRecordT(relayInfo)
3✔
148
        }
3✔
149

150
        if val, ok := typeMap[d.Constraints.TlvType()]; ok && val == nil {
6✔
151
                d.Constraints = tlv.SomeRecordT(constraints)
3✔
152
        }
3✔
153

154
        if val, ok := typeMap[d.Features.TlvType()]; ok && val == nil {
3✔
155
                d.Features = tlv.SomeRecordT(features)
×
156
        }
×
157

158
        return &d, nil
3✔
159
}
160

161
// EncodeBlindedRouteData encodes the blinded route data provided.
162
func EncodeBlindedRouteData(data *BlindedRouteData) ([]byte, error) {
×
163
        var (
×
164
                e               lnwire.ExtraOpaqueData
×
165
                recordProducers = make([]tlv.RecordProducer, 0, 5)
×
166
        )
×
167

×
168
        data.Padding.WhenSome(func(p tlv.RecordT[tlv.TlvType1, []byte]) {
×
169
                recordProducers = append(recordProducers, &p)
×
170
        })
×
171

172
        data.ShortChannelID.WhenSome(func(scid tlv.RecordT[tlv.TlvType2,
×
173
                lnwire.ShortChannelID]) {
×
174

×
175
                recordProducers = append(recordProducers, &scid)
×
176
        })
×
177

178
        data.PathID.WhenSome(func(pathID tlv.RecordT[tlv.TlvType6, []byte]) {
×
179
                recordProducers = append(recordProducers, &pathID)
×
180
        })
×
181

182
        data.NextBlindingOverride.WhenSome(func(pk tlv.RecordT[tlv.TlvType8,
×
183
                *btcec.PublicKey]) {
×
184

×
185
                recordProducers = append(recordProducers, &pk)
×
186
        })
×
187

188
        data.RelayInfo.WhenSome(func(r tlv.RecordT[tlv.TlvType10,
×
189
                PaymentRelayInfo]) {
×
190

×
191
                recordProducers = append(recordProducers, &r)
×
192
        })
×
193

194
        data.Constraints.WhenSome(func(cs tlv.RecordT[tlv.TlvType12,
×
195
                PaymentConstraints]) {
×
196

×
197
                recordProducers = append(recordProducers, &cs)
×
198
        })
×
199

200
        data.Features.WhenSome(func(f tlv.RecordT[tlv.TlvType14,
×
201
                lnwire.FeatureVector]) {
×
202

×
203
                recordProducers = append(recordProducers, &f)
×
204
        })
×
205

206
        if err := e.PackRecords(recordProducers...); err != nil {
×
207
                return nil, err
×
208
        }
×
209

210
        return e[:], nil
×
211
}
212

213
// PadBy adds "n" padding bytes to the BlindedRouteData using the Padding field.
214
// Callers should be aware that the total payload size will change by more than
215
// "n" since the "n" bytes will be prefixed by BigSize type and length fields.
216
// Callers may need to call PadBy iteratively until each encrypted data packet
217
// is the same size and so each call will overwrite the Padding record.
218
// Note that calling PadBy with an n value of 0 will still result in a zero
219
// length TLV entry being added.
220
func (b *BlindedRouteData) PadBy(n int) {
×
221
        b.Padding = tlv.SomeRecordT(
×
222
                tlv.NewPrimitiveRecord[tlv.TlvType1](make([]byte, n)),
×
223
        )
×
224
}
×
225

226
// PaymentRelayInfo describes the relay policy for a blinded path.
227
type PaymentRelayInfo struct {
228
        // CltvExpiryDelta is the expiry delta for the payment.
229
        CltvExpiryDelta uint16
230

231
        // FeeRate is the fee rate that will be charged per millionth of a
232
        // satoshi.
233
        FeeRate uint32
234

235
        // BaseFee is the per-htlc fee charged.
236
        BaseFee uint32
237
}
238

239
// Record creates a tlv.Record that encodes the payment relay (type 10) type for
240
// an encrypted blob payload.
241
func (i *PaymentRelayInfo) Record() tlv.Record {
3✔
242
        return tlv.MakeDynamicRecord(
3✔
243
                10, &i, func() uint64 {
3✔
244
                        // uint16 + uint32 + tuint32
×
245
                        return 2 + 4 + tlv.SizeTUint32(i.BaseFee)
×
246
                }, encodePaymentRelay, decodePaymentRelay,
×
247
        )
248
}
249

250
func encodePaymentRelay(w io.Writer, val interface{}, buf *[8]byte) error {
×
251
        if t, ok := val.(**PaymentRelayInfo); ok {
×
252
                relayInfo := *t
×
253

×
254
                // Just write our first 6 bytes directly.
×
255
                binary.BigEndian.PutUint16(buf[:2], relayInfo.CltvExpiryDelta)
×
256
                binary.BigEndian.PutUint32(buf[2:6], relayInfo.FeeRate)
×
257
                if _, err := w.Write(buf[0:6]); err != nil {
×
258
                        return err
×
259
                }
×
260

261
                // We can safely reuse buf here because we overwrite its
262
                // contents.
263
                return tlv.ETUint32(w, &relayInfo.BaseFee, buf)
×
264
        }
265

266
        return tlv.NewTypeForEncodingErr(val, "**hop.PaymentRelayInfo")
×
267
}
268

269
func decodePaymentRelay(r io.Reader, val interface{}, buf *[8]byte,
270
        l uint64) error {
3✔
271

3✔
272
        if t, ok := val.(**PaymentRelayInfo); ok && l <= 10 {
6✔
273
                scratch := make([]byte, l)
3✔
274

3✔
275
                n, err := io.ReadFull(r, scratch)
3✔
276
                if err != nil {
3✔
277
                        return err
×
278
                }
×
279

280
                // We expect at least 6 bytes, because we have 2 bytes for
281
                // cltv delta and 4 bytes for fee rate.
282
                if n < 6 {
3✔
283
                        return tlv.NewTypeForDecodingErr(val,
×
284
                                "*hop.paymentRelayInfo", uint64(n), 6)
×
285
                }
×
286

287
                relayInfo := *t
3✔
288

3✔
289
                relayInfo.CltvExpiryDelta = binary.BigEndian.Uint16(
3✔
290
                        scratch[0:2],
3✔
291
                )
3✔
292
                relayInfo.FeeRate = binary.BigEndian.Uint32(scratch[2:6])
3✔
293

3✔
294
                // To be able to re-use the DTUint32 function we create a
3✔
295
                // buffer with just the bytes holding the variable length u32.
3✔
296
                // If the base fee is zero, this will be an empty buffer, which
3✔
297
                // is okay.
3✔
298
                b := bytes.NewBuffer(scratch[6:])
3✔
299

3✔
300
                return tlv.DTUint32(b, &relayInfo.BaseFee, buf, l-6)
3✔
301
        }
302

303
        return tlv.NewTypeForDecodingErr(val, "*hop.paymentRelayInfo", l, 10)
×
304
}
305

306
// PaymentConstraints is a set of restrictions on a payment.
307
type PaymentConstraints struct {
308
        // MaxCltvExpiry is the maximum expiry height for the payment.
309
        MaxCltvExpiry uint32
310

311
        // HtlcMinimumMsat is the minimum htlc size for the payment.
312
        HtlcMinimumMsat lnwire.MilliSatoshi
313
}
314

315
func (p *PaymentConstraints) Record() tlv.Record {
3✔
316
        return tlv.MakeDynamicRecord(
3✔
317
                12, &p, func() uint64 {
3✔
318
                        // uint32 + tuint64.
×
319
                        return 4 + tlv.SizeTUint64(uint64(
×
320
                                p.HtlcMinimumMsat,
×
321
                        ))
×
322
                },
×
323
                encodePaymentConstraints, decodePaymentConstraints,
324
        )
325
}
326

327
func encodePaymentConstraints(w io.Writer, val interface{},
328
        buf *[8]byte) error {
×
329

×
330
        if c, ok := val.(**PaymentConstraints); ok {
×
331
                constraints := *c
×
332

×
333
                binary.BigEndian.PutUint32(buf[:4], constraints.MaxCltvExpiry)
×
334
                if _, err := w.Write(buf[:4]); err != nil {
×
335
                        return err
×
336
                }
×
337

338
                // We can safely re-use buf here because we overwrite its
339
                // contents.
340
                htlcMsat := uint64(constraints.HtlcMinimumMsat)
×
341

×
342
                return tlv.ETUint64(w, &htlcMsat, buf)
×
343
        }
344

345
        return tlv.NewTypeForEncodingErr(val, "**PaymentConstraints")
×
346
}
347

348
func decodePaymentConstraints(r io.Reader, val interface{}, buf *[8]byte,
349
        l uint64) error {
3✔
350

3✔
351
        if c, ok := val.(**PaymentConstraints); ok && l <= 12 {
6✔
352
                scratch := make([]byte, l)
3✔
353

3✔
354
                n, err := io.ReadFull(r, scratch)
3✔
355
                if err != nil {
3✔
356
                        return err
×
357
                }
×
358

359
                // We expect at least 4 bytes for our uint32.
360
                if n < 4 {
3✔
361
                        return tlv.NewTypeForDecodingErr(val,
×
362
                                "*paymentConstraints", uint64(n), 4)
×
363
                }
×
364

365
                payConstraints := *c
3✔
366

3✔
367
                payConstraints.MaxCltvExpiry = binary.BigEndian.Uint32(
3✔
368
                        scratch[:4],
3✔
369
                )
3✔
370

3✔
371
                // This could be empty if our minimum is zero, that's okay.
3✔
372
                var (
3✔
373
                        b       = bytes.NewBuffer(scratch[4:])
3✔
374
                        minHtlc uint64
3✔
375
                )
3✔
376

3✔
377
                err = tlv.DTUint64(b, &minHtlc, buf, l-4)
3✔
378
                if err != nil {
3✔
379
                        return err
×
380
                }
×
381
                payConstraints.HtlcMinimumMsat = lnwire.MilliSatoshi(minHtlc)
3✔
382

3✔
383
                return nil
3✔
384
        }
385

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