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

lightningnetwork / lnd / 14708967365

28 Apr 2025 01:27PM UTC coverage: 69.038% (+10.4%) from 58.592%
14708967365

Pull #9770

github

web-flow
Merge 70f4cba9c into b34afa33f
Pull Request #9770: routing+migration: fix mission control migration with nil failure msg

45 of 51 new or added lines in 3 files covered. (88.24%)

51 existing lines in 11 files now uncovered.

133929 of 193993 relevant lines covered (69.04%)

22158.98 hits per line

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

65.93
/channeldb/migration32/mission_control_store.go
1
package migration32
2

3
import (
4
        "bytes"
5
        "io"
6
        "math"
7
        "time"
8

9
        "github.com/btcsuite/btcd/wire"
10
        lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
11
        "github.com/lightningnetwork/lnd/fn/v2"
12
        "github.com/lightningnetwork/lnd/tlv"
13
)
14

15
const (
16
        // unknownFailureSourceIdx is the database encoding of an unknown error
17
        // source.
18
        unknownFailureSourceIdx = -1
19
)
20

21
var (
22
        // resultsKey is the fixed key under which the attempt results are
23
        // stored.
24
        resultsKey = []byte("missioncontrol-results")
25
)
26

27
// paymentResultOld is the information that becomes available when a payment
28
// attempt completes.
29
type paymentResultOld struct {
30
        id                 uint64
31
        timeFwd, timeReply time.Time
32
        route              *Route
33
        success            bool
34
        failureSourceIdx   *int
35
        failure            lnwire.FailureMessage
36
}
37

38
// deserializeOldResult deserializes a payment result using the old encoding.
39
func deserializeOldResult(k, v []byte) (*paymentResultOld, error) {
3✔
40
        // Parse payment id.
3✔
41
        result := paymentResultOld{
3✔
42
                id: byteOrder.Uint64(k[8:]),
3✔
43
        }
3✔
44

3✔
45
        r := bytes.NewReader(v)
3✔
46

3✔
47
        // Read timestamps, success status and failure source index.
3✔
48
        var (
3✔
49
                timeFwd, timeReply uint64
3✔
50
                dbFailureSourceIdx int32
3✔
51
        )
3✔
52

3✔
53
        err := ReadElements(
3✔
54
                r, &timeFwd, &timeReply, &result.success, &dbFailureSourceIdx,
3✔
55
        )
3✔
56
        if err != nil {
3✔
57
                return nil, err
×
58
        }
×
59

60
        // Convert time stamps to local time zone for consistent logging.
61
        result.timeFwd = time.Unix(0, int64(timeFwd)).Local()
3✔
62
        result.timeReply = time.Unix(0, int64(timeReply)).Local()
3✔
63

3✔
64
        // Convert from unknown index magic number to nil value.
3✔
65
        if dbFailureSourceIdx != unknownFailureSourceIdx {
5✔
66
                failureSourceIdx := int(dbFailureSourceIdx)
2✔
67
                result.failureSourceIdx = &failureSourceIdx
2✔
68
        }
2✔
69

70
        // Read route.
71
        route, err := DeserializeRoute(r)
3✔
72
        if err != nil {
3✔
73
                return nil, err
×
74
        }
×
75
        result.route = &route
3✔
76

3✔
77
        // Read failure.
3✔
78
        failureBytes, err := wire.ReadVarBytes(r, 0, math.MaxUint16, "failure")
3✔
79
        if err != nil {
3✔
80
                return nil, err
×
81
        }
×
82
        if len(failureBytes) > 0 {
4✔
83
                result.failure, err = lnwire.DecodeFailureMessage(
1✔
84
                        bytes.NewReader(failureBytes), 0,
1✔
85
                )
1✔
86
                if err != nil {
1✔
87
                        return nil, err
×
88
                }
×
89
        }
90

91
        return &result, nil
3✔
92
}
93

94
// convertPaymentResult converts a paymentResultOld to a paymentResultNew.
95
func convertPaymentResult(old *paymentResultOld) *paymentResultNew {
3✔
96
        var failure *paymentFailure
3✔
97
        if !old.success {
5✔
98
                failure = newPaymentFailure(old.failureSourceIdx, old.failure)
2✔
99
        }
2✔
100

101
        return newPaymentResult(
3✔
102
                old.id, extractMCRoute(old.route), old.timeFwd, old.timeReply,
3✔
103
                failure,
3✔
104
        )
3✔
105
}
106

107
// newPaymentResult constructs a new paymentResult.
108
func newPaymentResult(id uint64, rt *mcRoute, timeFwd, timeReply time.Time,
109
        failure *paymentFailure) *paymentResultNew {
3✔
110

3✔
111
        result := &paymentResultNew{
3✔
112
                id: id,
3✔
113
                timeFwd: tlv.NewPrimitiveRecord[tlv.TlvType0](
3✔
114
                        uint64(timeFwd.UnixNano()),
3✔
115
                ),
3✔
116
                timeReply: tlv.NewPrimitiveRecord[tlv.TlvType1](
3✔
117
                        uint64(timeReply.UnixNano()),
3✔
118
                ),
3✔
119
                route: tlv.NewRecordT[tlv.TlvType2](*rt),
3✔
120
        }
3✔
121

3✔
122
        if failure != nil {
5✔
123
                result.failure = tlv.SomeRecordT(
2✔
124
                        tlv.NewRecordT[tlv.TlvType3](*failure),
2✔
125
                )
2✔
126
        }
2✔
127

128
        return result
3✔
129
}
130

131
// paymentResultNew is the information that becomes available when a payment
132
// attempt completes.
133
type paymentResultNew struct {
134
        id        uint64
135
        timeFwd   tlv.RecordT[tlv.TlvType0, uint64]
136
        timeReply tlv.RecordT[tlv.TlvType1, uint64]
137
        route     tlv.RecordT[tlv.TlvType2, mcRoute]
138

139
        // failure holds information related to the failure of a payment. The
140
        // presence of this record indicates a payment failure. The absence of
141
        // this record indicates a successful payment.
142
        failure tlv.OptionalRecordT[tlv.TlvType3, paymentFailure]
143
}
144

145
// paymentFailure represents the presence of a payment failure. It may or may
146
// not include additional information about said failure.
147
type paymentFailure struct {
148
        info tlv.OptionalRecordT[tlv.TlvType0, paymentFailureInfo]
149
}
150

151
// newPaymentFailure constructs a new paymentFailure struct. If the source
152
// index is nil, then an empty paymentFailure is returned. This represents a
153
// failure with unknown details. Otherwise, the index and failure message are
154
// used to populate the info field of the paymentFailure.
155
func newPaymentFailure(sourceIdx *int,
156
        failureMsg lnwire.FailureMessage) *paymentFailure {
4✔
157

4✔
158
        if sourceIdx == nil {
4✔
159
                return &paymentFailure{}
×
160
        }
×
161

162
        info := paymentFailureInfo{
4✔
163
                sourceIdx: tlv.NewPrimitiveRecord[tlv.TlvType0](
4✔
164
                        uint8(*sourceIdx),
4✔
165
                ),
4✔
166
        }
4✔
167

4✔
168
        if failureMsg != nil {
6✔
169
                info.msg = tlv.SomeRecordT(
2✔
170
                        tlv.NewRecordT[tlv.TlvType1](
2✔
171
                                failureMessage{failureMsg},
2✔
172
                        ),
2✔
173
                )
2✔
174
        }
2✔
175

176
        return &paymentFailure{
4✔
177
                info: tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType0](info)),
4✔
178
        }
4✔
179
}
180

181
// Record returns a TLV record that can be used to encode/decode a
182
// paymentFailure to/from a TLV stream.
183
func (r *paymentFailure) Record() tlv.Record {
4✔
184
        recordSize := func() uint64 {
8✔
185
                var (
4✔
186
                        b   bytes.Buffer
4✔
187
                        buf [8]byte
4✔
188
                )
4✔
189
                if err := encodePaymentFailure(&b, r, &buf); err != nil {
4✔
190
                        panic(err)
×
191
                }
192

193
                return uint64(len(b.Bytes()))
4✔
194
        }
195

196
        return tlv.MakeDynamicRecord(
4✔
197
                0, r, recordSize, encodePaymentFailure, decodePaymentFailure,
4✔
198
        )
4✔
199
}
200

201
func encodePaymentFailure(w io.Writer, val interface{}, _ *[8]byte) error {
8✔
202
        if v, ok := val.(*paymentFailure); ok {
16✔
203
                var recordProducers []tlv.RecordProducer
8✔
204
                v.info.WhenSome(
8✔
205
                        func(r tlv.RecordT[tlv.TlvType0, paymentFailureInfo]) {
16✔
206
                                recordProducers = append(recordProducers, &r)
8✔
207
                        },
8✔
208
                )
209

210
                return lnwire.EncodeRecordsTo(
8✔
211
                        w, lnwire.ProduceRecordsSorted(recordProducers...),
8✔
212
                )
8✔
213
        }
214

215
        return tlv.NewTypeForEncodingErr(val, "routing.paymentFailure")
×
216
}
217

218
func decodePaymentFailure(r io.Reader, val interface{}, _ *[8]byte,
219
        l uint64) error {
×
220

×
221
        if v, ok := val.(*paymentFailure); ok {
×
222
                var h paymentFailure
×
223

×
224
                info := tlv.ZeroRecordT[tlv.TlvType0, paymentFailureInfo]()
×
225
                typeMap, err := lnwire.DecodeRecords(
×
226
                        r, lnwire.ProduceRecordsSorted(&info)...,
×
227
                )
×
228
                if err != nil {
×
229
                        return err
×
230
                }
×
231

232
                if _, ok := typeMap[h.info.TlvType()]; ok {
×
233
                        h.info = tlv.SomeRecordT(info)
×
234
                }
×
235

236
                *v = h
×
237

×
238
                return nil
×
239
        }
240

241
        return tlv.NewTypeForDecodingErr(val, "routing.paymentFailure", l, l)
×
242
}
243

244
// paymentFailureInfo holds additional information about a payment failure.
245
type paymentFailureInfo struct {
246
        sourceIdx tlv.RecordT[tlv.TlvType0, uint8]
247
        msg       tlv.OptionalRecordT[tlv.TlvType1, failureMessage]
248
}
249

250
// Record returns a TLV record that can be used to encode/decode a
251
// paymentFailureInfo to/from a TLV stream.
252
func (r *paymentFailureInfo) Record() tlv.Record {
8✔
253
        recordSize := func() uint64 {
16✔
254
                var (
8✔
255
                        b   bytes.Buffer
8✔
256
                        buf [8]byte
8✔
257
                )
8✔
258
                if err := encodePaymentFailureInfo(&b, r, &buf); err != nil {
8✔
259
                        panic(err)
×
260
                }
261

262
                return uint64(len(b.Bytes()))
8✔
263
        }
264

265
        return tlv.MakeDynamicRecord(
8✔
266
                0, r, recordSize, encodePaymentFailureInfo,
8✔
267
                decodePaymentFailureInfo,
8✔
268
        )
8✔
269
}
270

271
func encodePaymentFailureInfo(w io.Writer, val interface{}, _ *[8]byte) error {
16✔
272
        if v, ok := val.(*paymentFailureInfo); ok {
32✔
273
                recordProducers := []tlv.RecordProducer{
16✔
274
                        &v.sourceIdx,
16✔
275
                }
16✔
276
                v.msg.WhenSome(
16✔
277
                        func(r tlv.RecordT[tlv.TlvType1, failureMessage]) {
24✔
278
                                recordProducers = append(recordProducers, &r)
8✔
279
                        },
8✔
280
                )
281

282
                return lnwire.EncodeRecordsTo(
16✔
283
                        w, lnwire.ProduceRecordsSorted(recordProducers...),
16✔
284
                )
16✔
285
        }
286

287
        return tlv.NewTypeForEncodingErr(val, "routing.paymentFailureInfo")
×
288
}
289

290
func decodePaymentFailureInfo(r io.Reader, val interface{}, _ *[8]byte,
291
        l uint64) error {
×
292

×
293
        if v, ok := val.(*paymentFailureInfo); ok {
×
294
                var h paymentFailureInfo
×
295

×
NEW
296
                msg := tlv.ZeroRecordT[tlv.TlvType1, failureMessage]()
×
NEW
297
                typeMap, err := lnwire.DecodeRecords(
×
298
                        r,
×
NEW
299
                        lnwire.ProduceRecordsSorted(&h.sourceIdx, &msg)...,
×
300
                )
×
301
                if err != nil {
×
302
                        return err
×
303
                }
×
304

NEW
305
                if _, ok := typeMap[h.msg.TlvType()]; ok {
×
NEW
306
                        h.msg = tlv.SomeRecordT(msg)
×
NEW
307
                }
×
308

309
                *v = h
×
310

×
311
                return nil
×
312
        }
313

314
        return tlv.NewTypeForDecodingErr(
×
315
                val, "routing.paymentFailureInfo", l, l,
×
316
        )
×
317
}
318

319
type failureMessage struct {
320
        lnwire.FailureMessage
321
}
322

323
// Record returns a TLV record that can be used to encode/decode a list of
324
// failureMessage to/from a TLV stream.
325
func (r *failureMessage) Record() tlv.Record {
8✔
326
        recordSize := func() uint64 {
16✔
327
                var (
8✔
328
                        b   bytes.Buffer
8✔
329
                        buf [8]byte
8✔
330
                )
8✔
331
                if err := encodeFailureMessage(&b, r, &buf); err != nil {
8✔
332
                        panic(err)
×
333
                }
334

335
                return uint64(len(b.Bytes()))
8✔
336
        }
337

338
        return tlv.MakeDynamicRecord(
8✔
339
                0, r, recordSize, encodeFailureMessage, decodeFailureMessage,
8✔
340
        )
8✔
341
}
342

343
func encodeFailureMessage(w io.Writer, val interface{}, _ *[8]byte) error {
16✔
344
        if v, ok := val.(*failureMessage); ok {
32✔
345
                var b bytes.Buffer
16✔
346
                err := lnwire.EncodeFailureMessage(&b, v.FailureMessage, 0)
16✔
347
                if err != nil {
16✔
348
                        return err
×
349
                }
×
350

351
                _, err = w.Write(b.Bytes())
16✔
352

16✔
353
                return err
16✔
354
        }
355

356
        return tlv.NewTypeForEncodingErr(val, "routing.failureMessage")
×
357
}
358

359
func decodeFailureMessage(r io.Reader, val interface{}, _ *[8]byte,
360
        l uint64) error {
×
361

×
362
        if v, ok := val.(*failureMessage); ok {
×
363
                msg, err := lnwire.DecodeFailureMessage(r, 0)
×
364
                if err != nil {
×
365
                        return err
×
366
                }
×
367

368
                *v = failureMessage{
×
369
                        FailureMessage: msg,
×
370
                }
×
371

×
372
                return nil
×
373
        }
374

375
        return tlv.NewTypeForDecodingErr(val, "routing.failureMessage", l, l)
×
376
}
377

378
// extractMCRoute extracts the fields required by MC from the Route struct to
379
// create the more minimal mcRoute struct.
380
func extractMCRoute(r *Route) *mcRoute {
3✔
381
        return &mcRoute{
3✔
382
                sourcePubKey: tlv.NewRecordT[tlv.TlvType0](r.SourcePubKey),
3✔
383
                totalAmount:  tlv.NewRecordT[tlv.TlvType1](r.TotalAmount),
3✔
384
                hops: tlv.NewRecordT[tlv.TlvType2](
3✔
385
                        extractMCHops(r.Hops),
3✔
386
                ),
3✔
387
        }
3✔
388
}
3✔
389

390
// extractMCHops extracts the Hop fields that MC actually uses from a slice of
391
// Hops.
392
func extractMCHops(hops []*Hop) mcHops {
3✔
393
        return fn.Map(hops, extractMCHop)
3✔
394
}
3✔
395

396
// extractMCHop extracts the Hop fields that MC actually uses from a Hop.
397
func extractMCHop(hop *Hop) *mcHop {
6✔
398
        h := mcHop{
6✔
399
                channelID: tlv.NewPrimitiveRecord[tlv.TlvType0, uint64](
6✔
400
                        hop.ChannelID,
6✔
401
                ),
6✔
402
                pubKeyBytes: tlv.NewRecordT[tlv.TlvType1, Vertex](
6✔
403
                        hop.PubKeyBytes,
6✔
404
                ),
6✔
405
                amtToFwd: tlv.NewRecordT[tlv.TlvType2, lnwire.MilliSatoshi](
6✔
406
                        hop.AmtToForward,
6✔
407
                ),
6✔
408
        }
6✔
409

6✔
410
        if hop.BlindingPoint != nil {
10✔
411
                h.hasBlindingPoint = tlv.SomeRecordT(
4✔
412
                        tlv.NewRecordT[tlv.TlvType3, lnwire.TrueBoolean](
4✔
413
                                lnwire.TrueBoolean{},
4✔
414
                        ),
4✔
415
                )
4✔
416
        }
4✔
417

418
        if len(hop.CustomRecords) != 0 {
10✔
419
                h.hasCustomRecords = tlv.SomeRecordT(
4✔
420
                        tlv.NewRecordT[tlv.TlvType4, lnwire.TrueBoolean](
4✔
421
                                lnwire.TrueBoolean{},
4✔
422
                        ),
4✔
423
                )
4✔
424
        }
4✔
425

426
        return &h
6✔
427
}
428

429
// mcRoute holds the bare minimum info about a payment attempt route that MC
430
// requires.
431
type mcRoute struct {
432
        sourcePubKey tlv.RecordT[tlv.TlvType0, Vertex]
433
        totalAmount  tlv.RecordT[tlv.TlvType1, lnwire.MilliSatoshi]
434
        hops         tlv.RecordT[tlv.TlvType2, mcHops]
435
}
436

437
// Record returns a TLV record that can be used to encode/decode an mcRoute
438
// to/from a TLV stream.
439
func (r *mcRoute) Record() tlv.Record {
6✔
440
        recordSize := func() uint64 {
12✔
441
                var (
6✔
442
                        b   bytes.Buffer
6✔
443
                        buf [8]byte
6✔
444
                )
6✔
445
                if err := encodeMCRoute(&b, r, &buf); err != nil {
6✔
446
                        panic(err)
×
447
                }
448

449
                return uint64(len(b.Bytes()))
6✔
450
        }
451

452
        return tlv.MakeDynamicRecord(
6✔
453
                0, r, recordSize, encodeMCRoute, decodeMCRoute,
6✔
454
        )
6✔
455
}
456

457
func encodeMCRoute(w io.Writer, val interface{}, _ *[8]byte) error {
12✔
458
        if v, ok := val.(*mcRoute); ok {
24✔
459
                return serializeRoute(w, v)
12✔
460
        }
12✔
461

462
        return tlv.NewTypeForEncodingErr(val, "routing.mcRoute")
×
463
}
464

465
func decodeMCRoute(r io.Reader, val interface{}, _ *[8]byte, l uint64) error {
×
466
        if v, ok := val.(*mcRoute); ok {
×
467
                route, err := deserializeRoute(io.LimitReader(r, int64(l)))
×
468
                if err != nil {
×
469
                        return err
×
470
                }
×
471

472
                *v = *route
×
473

×
474
                return nil
×
475
        }
476

477
        return tlv.NewTypeForDecodingErr(val, "routing.mcRoute", l, l)
×
478
}
479

480
// mcHops is a list of mcHop records.
481
type mcHops []*mcHop
482

483
// Record returns a TLV record that can be used to encode/decode a list of
484
// mcHop to/from a TLV stream.
485
func (h *mcHops) Record() tlv.Record {
12✔
486
        recordSize := func() uint64 {
24✔
487
                var (
12✔
488
                        b   bytes.Buffer
12✔
489
                        buf [8]byte
12✔
490
                )
12✔
491
                if err := encodeMCHops(&b, h, &buf); err != nil {
12✔
492
                        panic(err)
×
493
                }
494

495
                return uint64(len(b.Bytes()))
12✔
496
        }
497

498
        return tlv.MakeDynamicRecord(
12✔
499
                0, h, recordSize, encodeMCHops, decodeMCHops,
12✔
500
        )
12✔
501
}
502

503
func encodeMCHops(w io.Writer, val interface{}, buf *[8]byte) error {
24✔
504
        if v, ok := val.(*mcHops); ok {
48✔
505
                // Encode the number of hops as a var int.
24✔
506
                if err := tlv.WriteVarInt(w, uint64(len(*v)), buf); err != nil {
24✔
507
                        return err
×
508
                }
×
509

510
                // With that written out, we'll now encode the entries
511
                // themselves as a sub-TLV record, which includes its _own_
512
                // inner length prefix.
513
                for _, hop := range *v {
72✔
514
                        var hopBytes bytes.Buffer
48✔
515
                        if err := serializeNewHop(&hopBytes, hop); err != nil {
48✔
516
                                return err
×
517
                        }
×
518

519
                        // We encode the record with a varint length followed by
520
                        // the _raw_ TLV bytes.
521
                        tlvLen := uint64(len(hopBytes.Bytes()))
48✔
522
                        if err := tlv.WriteVarInt(w, tlvLen, buf); err != nil {
48✔
523
                                return err
×
524
                        }
×
525

526
                        if _, err := w.Write(hopBytes.Bytes()); err != nil {
48✔
527
                                return err
×
528
                        }
×
529
                }
530

531
                return nil
24✔
532
        }
533

534
        return tlv.NewTypeForEncodingErr(val, "routing.mcHops")
×
535
}
536

537
func decodeMCHops(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
×
538
        if v, ok := val.(*mcHops); ok {
×
539
                // First, we'll decode the varint that encodes how many hops
×
540
                // are encoded in the stream.
×
541
                numHops, err := tlv.ReadVarInt(r, buf)
×
542
                if err != nil {
×
543
                        return err
×
544
                }
×
545

546
                // Now that we know how many records we'll need to read, we can
547
                // iterate and read them all out in series.
548
                for i := uint64(0); i < numHops; i++ {
×
549
                        // Read out the varint that encodes the size of this
×
550
                        // inner TLV record.
×
551
                        hopSize, err := tlv.ReadVarInt(r, buf)
×
552
                        if err != nil {
×
553
                                return err
×
554
                        }
×
555

556
                        // Using this information, we'll create a new limited
557
                        // reader that'll return an EOF once the end has been
558
                        // reached so the stream stops consuming bytes.
559
                        innerTlvReader := &io.LimitedReader{
×
560
                                R: r,
×
561
                                N: int64(hopSize),
×
562
                        }
×
563

×
564
                        hop, err := deserializeNewHop(innerTlvReader)
×
565
                        if err != nil {
×
566
                                return err
×
567
                        }
×
568

569
                        *v = append(*v, hop)
×
570
                }
571

572
                return nil
×
573
        }
574

575
        return tlv.NewTypeForDecodingErr(val, "routing.mcHops", l, l)
×
576
}
577

578
// serializeRoute serializes a mcRoute and writes the resulting bytes to the
579
// given io.Writer.
580
func serializeRoute(w io.Writer, r *mcRoute) error {
12✔
581
        records := lnwire.ProduceRecordsSorted(
12✔
582
                &r.sourcePubKey,
12✔
583
                &r.totalAmount,
12✔
584
                &r.hops,
12✔
585
        )
12✔
586

12✔
587
        return lnwire.EncodeRecordsTo(w, records)
12✔
588
}
12✔
589

590
// deserializeRoute deserializes the mcRoute from the given io.Reader.
591
func deserializeRoute(r io.Reader) (*mcRoute, error) {
×
592
        var rt mcRoute
×
593
        records := lnwire.ProduceRecordsSorted(
×
594
                &rt.sourcePubKey,
×
595
                &rt.totalAmount,
×
596
                &rt.hops,
×
597
        )
×
598

×
599
        _, err := lnwire.DecodeRecords(r, records...)
×
600
        if err != nil {
×
601
                return nil, err
×
602
        }
×
603

604
        return &rt, nil
×
605
}
606

607
// deserializeNewHop deserializes the mcHop from the given io.Reader.
608
func deserializeNewHop(r io.Reader) (*mcHop, error) {
×
609
        var (
×
610
                h        mcHop
×
611
                blinding = tlv.ZeroRecordT[tlv.TlvType3, lnwire.TrueBoolean]()
×
612
                custom   = tlv.ZeroRecordT[tlv.TlvType4, lnwire.TrueBoolean]()
×
613
        )
×
614
        records := lnwire.ProduceRecordsSorted(
×
615
                &h.channelID,
×
616
                &h.pubKeyBytes,
×
617
                &h.amtToFwd,
×
618
                &blinding,
×
619
                &custom,
×
620
        )
×
621

×
622
        typeMap, err := lnwire.DecodeRecords(r, records...)
×
623
        if err != nil {
×
624
                return nil, err
×
625
        }
×
626

627
        if _, ok := typeMap[h.hasBlindingPoint.TlvType()]; ok {
×
628
                h.hasBlindingPoint = tlv.SomeRecordT(blinding)
×
629
        }
×
630

631
        if _, ok := typeMap[h.hasCustomRecords.TlvType()]; ok {
×
632
                h.hasCustomRecords = tlv.SomeRecordT(custom)
×
633
        }
×
634

635
        return &h, nil
×
636
}
637

638
// serializeNewHop serializes a mcHop and writes the resulting bytes to the
639
// given io.Writer.
640
func serializeNewHop(w io.Writer, h *mcHop) error {
48✔
641
        recordProducers := []tlv.RecordProducer{
48✔
642
                &h.channelID,
48✔
643
                &h.pubKeyBytes,
48✔
644
                &h.amtToFwd,
48✔
645
        }
48✔
646

48✔
647
        h.hasBlindingPoint.WhenSome(func(
48✔
648
                hasBlinding tlv.RecordT[tlv.TlvType3, lnwire.TrueBoolean]) {
80✔
649

32✔
650
                recordProducers = append(recordProducers, &hasBlinding)
32✔
651
        })
32✔
652

653
        h.hasCustomRecords.WhenSome(func(
48✔
654
                hasCustom tlv.RecordT[tlv.TlvType4, lnwire.TrueBoolean]) {
80✔
655

32✔
656
                recordProducers = append(recordProducers, &hasCustom)
32✔
657
        })
32✔
658

659
        return lnwire.EncodeRecordsTo(
48✔
660
                w, lnwire.ProduceRecordsSorted(recordProducers...),
48✔
661
        )
48✔
662
}
663

664
// mcHop holds the bare minimum info about a payment attempt route hop that MC
665
// requires.
666
type mcHop struct {
667
        channelID        tlv.RecordT[tlv.TlvType0, uint64]
668
        pubKeyBytes      tlv.RecordT[tlv.TlvType1, Vertex]
669
        amtToFwd         tlv.RecordT[tlv.TlvType2, lnwire.MilliSatoshi]
670
        hasBlindingPoint tlv.OptionalRecordT[tlv.TlvType3, lnwire.TrueBoolean]
671
        hasCustomRecords tlv.OptionalRecordT[tlv.TlvType4, lnwire.TrueBoolean]
672
}
673

674
// serializeOldResult serializes a payment result and returns a key and value
675
// byte slice to insert into the bucket.
676
func serializeOldResult(rp *paymentResultOld) ([]byte, []byte, error) {
3✔
677
        // Write timestamps, success status, failure source index and route.
3✔
678
        var b bytes.Buffer
3✔
679
        var dbFailureSourceIdx int32
3✔
680
        if rp.failureSourceIdx == nil {
4✔
681
                dbFailureSourceIdx = unknownFailureSourceIdx
1✔
682
        } else {
3✔
683
                dbFailureSourceIdx = int32(*rp.failureSourceIdx)
2✔
684
        }
2✔
685
        err := WriteElements(
3✔
686
                &b,
3✔
687
                uint64(rp.timeFwd.UnixNano()),
3✔
688
                uint64(rp.timeReply.UnixNano()),
3✔
689
                rp.success, dbFailureSourceIdx,
3✔
690
        )
3✔
691
        if err != nil {
3✔
692
                return nil, nil, err
×
693
        }
×
694

695
        if err := SerializeRoute(&b, *rp.route); err != nil {
3✔
696
                return nil, nil, err
×
697
        }
×
698

699
        // Write failure. If there is no failure message, write an empty
700
        // byte slice.
701
        var failureBytes bytes.Buffer
3✔
702
        if rp.failure != nil {
4✔
703
                err := lnwire.EncodeFailureMessage(&failureBytes, rp.failure, 0)
1✔
704
                if err != nil {
1✔
705
                        return nil, nil, err
×
706
                }
×
707
        }
708
        err = wire.WriteVarBytes(&b, 0, failureBytes.Bytes())
3✔
709
        if err != nil {
3✔
710
                return nil, nil, err
×
711
        }
×
712
        // Compose key that identifies this result.
713
        key := getResultKeyOld(rp)
3✔
714

3✔
715
        return key, b.Bytes(), nil
3✔
716
}
717

718
// getResultKeyOld returns a byte slice representing a unique key for this
719
// payment result.
720
func getResultKeyOld(rp *paymentResultOld) []byte {
3✔
721
        var keyBytes [8 + 8 + 33]byte
3✔
722

3✔
723
        // Identify records by a combination of time, payment id and sender pub
3✔
724
        // key. This allows importing mission control data from an external
3✔
725
        // source without key collisions and keeps the records sorted
3✔
726
        // chronologically.
3✔
727
        byteOrder.PutUint64(keyBytes[:], uint64(rp.timeReply.UnixNano()))
3✔
728
        byteOrder.PutUint64(keyBytes[8:], rp.id)
3✔
729
        copy(keyBytes[16:], rp.route.SourcePubKey[:])
3✔
730

3✔
731
        return keyBytes[:]
3✔
732
}
3✔
733

734
// serializeNewResult serializes a payment result and returns a key and value
735
// byte slice to insert into the bucket.
736
func serializeNewResult(rp *paymentResultNew) ([]byte, []byte, error) {
6✔
737
        recordProducers := []tlv.RecordProducer{
6✔
738
                &rp.timeFwd,
6✔
739
                &rp.timeReply,
6✔
740
                &rp.route,
6✔
741
        }
6✔
742

6✔
743
        rp.failure.WhenSome(
6✔
744
                func(failure tlv.RecordT[tlv.TlvType3, paymentFailure]) {
10✔
745
                        recordProducers = append(recordProducers, &failure)
4✔
746
                },
4✔
747
        )
748

749
        // Compose key that identifies this result.
750
        key := getResultKeyNew(rp)
6✔
751

6✔
752
        var buff bytes.Buffer
6✔
753
        err := lnwire.EncodeRecordsTo(
6✔
754
                &buff, lnwire.ProduceRecordsSorted(recordProducers...),
6✔
755
        )
6✔
756
        if err != nil {
6✔
757
                return nil, nil, err
×
758
        }
×
759

760
        return key, buff.Bytes(), nil
6✔
761
}
762

763
// getResultKeyNew returns a byte slice representing a unique key for this
764
// payment result.
765
func getResultKeyNew(rp *paymentResultNew) []byte {
6✔
766
        var keyBytes [8 + 8 + 33]byte
6✔
767

6✔
768
        // Identify records by a combination of time, payment id and sender pub
6✔
769
        // key. This allows importing mission control data from an external
6✔
770
        // source without key collisions and keeps the records sorted
6✔
771
        // chronologically.
6✔
772
        byteOrder.PutUint64(keyBytes[:], rp.timeReply.Val)
6✔
773
        byteOrder.PutUint64(keyBytes[8:], rp.id)
6✔
774
        copy(keyBytes[16:], rp.route.Val.sourcePubKey.Val[:])
6✔
775

6✔
776
        return keyBytes[:]
6✔
777
}
6✔
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