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

lightningnetwork / lnd / 14729261246

29 Apr 2025 10:40AM UTC coverage: 69.044% (+10.5%) from 58.592%
14729261246

Pull #9770

github

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

64 of 71 new or added lines in 3 files covered. (90.14%)

22 existing lines in 6 files now uncovered.

133944 of 193997 relevant lines covered (69.04%)

22114.73 hits per line

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

66.13
/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 fn.Option[paymentFailure]
3✔
97
        if !old.success {
5✔
98
                failure = fn.Some(newPaymentFailure(
2✔
99
                        old.failureSourceIdx,
2✔
100
                        old.failure,
2✔
101
                ))
2✔
102
        }
2✔
103

104
        return newPaymentResult(
3✔
105
                old.id, extractMCRoute(old.route), old.timeFwd, old.timeReply,
3✔
106
                failure,
3✔
107
        )
3✔
108
}
109

110
// newPaymentResult constructs a new paymentResult.
111
func newPaymentResult(id uint64, rt *mcRoute, timeFwd, timeReply time.Time,
112
        failure fn.Option[paymentFailure]) *paymentResultNew {
3✔
113

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

3✔
125
        failure.WhenSome(func(f paymentFailure) {
5✔
126
                result.failure = tlv.SomeRecordT(
2✔
127
                        tlv.NewRecordT[tlv.TlvType3](f),
2✔
128
                )
2✔
129
        })
2✔
130

131
        return result
3✔
132
}
133

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

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

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

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

4✔
161
        if sourceIdx == nil {
4✔
NEW
162
                return paymentFailure{}
×
163
        }
×
164

165
        info := paymentFailureInfo{
4✔
166
                sourceIdx: tlv.NewPrimitiveRecord[tlv.TlvType0](
4✔
167
                        uint8(*sourceIdx),
4✔
168
                ),
4✔
169
        }
4✔
170

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

179
        return paymentFailure{
4✔
180
                info: tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType0](info)),
4✔
181
        }
4✔
182
}
183

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

196
                return uint64(len(b.Bytes()))
4✔
197
        }
198

199
        return tlv.MakeDynamicRecord(
4✔
200
                0, r, recordSize, encodePaymentFailure, decodePaymentFailure,
4✔
201
        )
4✔
202
}
203

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

213
                return lnwire.EncodeRecordsTo(
8✔
214
                        w, lnwire.ProduceRecordsSorted(recordProducers...),
8✔
215
                )
8✔
216
        }
217

218
        return tlv.NewTypeForEncodingErr(val, "routing.paymentFailure")
×
219
}
220

221
func decodePaymentFailure(r io.Reader, val interface{}, _ *[8]byte,
222
        l uint64) error {
×
223

×
224
        if v, ok := val.(*paymentFailure); ok {
×
225
                var h paymentFailure
×
226

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

235
                if _, ok := typeMap[h.info.TlvType()]; ok {
×
236
                        h.info = tlv.SomeRecordT(info)
×
237
                }
×
238

239
                *v = h
×
240

×
241
                return nil
×
242
        }
243

244
        return tlv.NewTypeForDecodingErr(val, "routing.paymentFailure", l, l)
×
245
}
246

247
// paymentFailureInfo holds additional information about a payment failure.
248
type paymentFailureInfo struct {
249
        // sourceIdx is the hop the error was reported from. To have a
250
        // meaningful payment failure, we need to have a source index. Otherwise
251
        // we indicate that there was no error information.
252
        sourceIdx tlv.RecordT[tlv.TlvType0, uint8]
253

254
        // msg is the error why a payment failed. If we identify the failure of
255
        // a certain hop, but aren't able to decode the failure message, we
256
        // don't set the failure message, which is why it is an optional record.
257
        msg tlv.OptionalRecordT[tlv.TlvType1, failureMessage]
258
}
259

260
// Record returns a TLV record that can be used to encode/decode a
261
// paymentFailureInfo to/from a TLV stream.
262
func (r *paymentFailureInfo) Record() tlv.Record {
8✔
263
        recordSize := func() uint64 {
16✔
264
                var (
8✔
265
                        b   bytes.Buffer
8✔
266
                        buf [8]byte
8✔
267
                )
8✔
268
                if err := encodePaymentFailureInfo(&b, r, &buf); err != nil {
8✔
269
                        panic(err)
×
270
                }
271

272
                return uint64(len(b.Bytes()))
8✔
273
        }
274

275
        return tlv.MakeDynamicRecord(
8✔
276
                0, r, recordSize, encodePaymentFailureInfo,
8✔
277
                decodePaymentFailureInfo,
8✔
278
        )
8✔
279
}
280

281
func encodePaymentFailureInfo(w io.Writer, val interface{}, _ *[8]byte) error {
16✔
282
        if v, ok := val.(*paymentFailureInfo); ok {
32✔
283
                recordProducers := []tlv.RecordProducer{
16✔
284
                        &v.sourceIdx,
16✔
285
                }
16✔
286
                v.msg.WhenSome(
16✔
287
                        func(r tlv.RecordT[tlv.TlvType1, failureMessage]) {
24✔
288
                                recordProducers = append(recordProducers, &r)
8✔
289
                        },
8✔
290
                )
291

292
                return lnwire.EncodeRecordsTo(
16✔
293
                        w, lnwire.ProduceRecordsSorted(recordProducers...),
16✔
294
                )
16✔
295
        }
296

297
        return tlv.NewTypeForEncodingErr(val, "routing.paymentFailureInfo")
×
298
}
299

300
func decodePaymentFailureInfo(r io.Reader, val interface{}, _ *[8]byte,
301
        l uint64) error {
×
302

×
303
        if v, ok := val.(*paymentFailureInfo); ok {
×
304
                var h paymentFailureInfo
×
305

×
NEW
306
                msg := tlv.ZeroRecordT[tlv.TlvType1, failureMessage]()
×
NEW
307
                typeMap, err := lnwire.DecodeRecords(
×
308
                        r,
×
NEW
309
                        lnwire.ProduceRecordsSorted(&h.sourceIdx, &msg)...,
×
310
                )
×
311
                if err != nil {
×
312
                        return err
×
313
                }
×
314

NEW
315
                if _, ok := typeMap[h.msg.TlvType()]; ok {
×
NEW
316
                        h.msg = tlv.SomeRecordT(msg)
×
NEW
317
                }
×
318

319
                *v = h
×
320

×
321
                return nil
×
322
        }
323

324
        return tlv.NewTypeForDecodingErr(
×
325
                val, "routing.paymentFailureInfo", l, l,
×
326
        )
×
327
}
328

329
type failureMessage struct {
330
        lnwire.FailureMessage
331
}
332

333
// Record returns a TLV record that can be used to encode/decode a list of
334
// failureMessage to/from a TLV stream.
335
func (r *failureMessage) Record() tlv.Record {
8✔
336
        recordSize := func() uint64 {
16✔
337
                var (
8✔
338
                        b   bytes.Buffer
8✔
339
                        buf [8]byte
8✔
340
                )
8✔
341
                if err := encodeFailureMessage(&b, r, &buf); err != nil {
8✔
342
                        panic(err)
×
343
                }
344

345
                return uint64(len(b.Bytes()))
8✔
346
        }
347

348
        return tlv.MakeDynamicRecord(
8✔
349
                0, r, recordSize, encodeFailureMessage, decodeFailureMessage,
8✔
350
        )
8✔
351
}
352

353
func encodeFailureMessage(w io.Writer, val interface{}, _ *[8]byte) error {
16✔
354
        if v, ok := val.(*failureMessage); ok {
32✔
355
                var b bytes.Buffer
16✔
356
                err := lnwire.EncodeFailureMessage(&b, v.FailureMessage, 0)
16✔
357
                if err != nil {
16✔
358
                        return err
×
359
                }
×
360

361
                _, err = w.Write(b.Bytes())
16✔
362

16✔
363
                return err
16✔
364
        }
365

366
        return tlv.NewTypeForEncodingErr(val, "routing.failureMessage")
×
367
}
368

369
func decodeFailureMessage(r io.Reader, val interface{}, _ *[8]byte,
370
        l uint64) error {
×
371

×
372
        if v, ok := val.(*failureMessage); ok {
×
373
                msg, err := lnwire.DecodeFailureMessage(r, 0)
×
374
                if err != nil {
×
375
                        return err
×
376
                }
×
377

378
                *v = failureMessage{
×
379
                        FailureMessage: msg,
×
380
                }
×
381

×
382
                return nil
×
383
        }
384

385
        return tlv.NewTypeForDecodingErr(val, "routing.failureMessage", l, l)
×
386
}
387

388
// extractMCRoute extracts the fields required by MC from the Route struct to
389
// create the more minimal mcRoute struct.
390
func extractMCRoute(r *Route) *mcRoute {
3✔
391
        return &mcRoute{
3✔
392
                sourcePubKey: tlv.NewRecordT[tlv.TlvType0](r.SourcePubKey),
3✔
393
                totalAmount:  tlv.NewRecordT[tlv.TlvType1](r.TotalAmount),
3✔
394
                hops: tlv.NewRecordT[tlv.TlvType2](
3✔
395
                        extractMCHops(r.Hops),
3✔
396
                ),
3✔
397
        }
3✔
398
}
3✔
399

400
// extractMCHops extracts the Hop fields that MC actually uses from a slice of
401
// Hops.
402
func extractMCHops(hops []*Hop) mcHops {
3✔
403
        return fn.Map(hops, extractMCHop)
3✔
404
}
3✔
405

406
// extractMCHop extracts the Hop fields that MC actually uses from a Hop.
407
func extractMCHop(hop *Hop) *mcHop {
6✔
408
        h := mcHop{
6✔
409
                channelID: tlv.NewPrimitiveRecord[tlv.TlvType0, uint64](
6✔
410
                        hop.ChannelID,
6✔
411
                ),
6✔
412
                pubKeyBytes: tlv.NewRecordT[tlv.TlvType1, Vertex](
6✔
413
                        hop.PubKeyBytes,
6✔
414
                ),
6✔
415
                amtToFwd: tlv.NewRecordT[tlv.TlvType2, lnwire.MilliSatoshi](
6✔
416
                        hop.AmtToForward,
6✔
417
                ),
6✔
418
        }
6✔
419

6✔
420
        if hop.BlindingPoint != nil {
10✔
421
                h.hasBlindingPoint = tlv.SomeRecordT(
4✔
422
                        tlv.NewRecordT[tlv.TlvType3, lnwire.TrueBoolean](
4✔
423
                                lnwire.TrueBoolean{},
4✔
424
                        ),
4✔
425
                )
4✔
426
        }
4✔
427

428
        if len(hop.CustomRecords) != 0 {
10✔
429
                h.hasCustomRecords = tlv.SomeRecordT(
4✔
430
                        tlv.NewRecordT[tlv.TlvType4, lnwire.TrueBoolean](
4✔
431
                                lnwire.TrueBoolean{},
4✔
432
                        ),
4✔
433
                )
4✔
434
        }
4✔
435

436
        return &h
6✔
437
}
438

439
// mcRoute holds the bare minimum info about a payment attempt route that MC
440
// requires.
441
type mcRoute struct {
442
        sourcePubKey tlv.RecordT[tlv.TlvType0, Vertex]
443
        totalAmount  tlv.RecordT[tlv.TlvType1, lnwire.MilliSatoshi]
444
        hops         tlv.RecordT[tlv.TlvType2, mcHops]
445
}
446

447
// Record returns a TLV record that can be used to encode/decode an mcRoute
448
// to/from a TLV stream.
449
func (r *mcRoute) Record() tlv.Record {
6✔
450
        recordSize := func() uint64 {
12✔
451
                var (
6✔
452
                        b   bytes.Buffer
6✔
453
                        buf [8]byte
6✔
454
                )
6✔
455
                if err := encodeMCRoute(&b, r, &buf); err != nil {
6✔
456
                        panic(err)
×
457
                }
458

459
                return uint64(len(b.Bytes()))
6✔
460
        }
461

462
        return tlv.MakeDynamicRecord(
6✔
463
                0, r, recordSize, encodeMCRoute, decodeMCRoute,
6✔
464
        )
6✔
465
}
466

467
func encodeMCRoute(w io.Writer, val interface{}, _ *[8]byte) error {
12✔
468
        if v, ok := val.(*mcRoute); ok {
24✔
469
                return serializeRoute(w, v)
12✔
470
        }
12✔
471

472
        return tlv.NewTypeForEncodingErr(val, "routing.mcRoute")
×
473
}
474

475
func decodeMCRoute(r io.Reader, val interface{}, _ *[8]byte, l uint64) error {
×
476
        if v, ok := val.(*mcRoute); ok {
×
477
                route, err := deserializeRoute(io.LimitReader(r, int64(l)))
×
478
                if err != nil {
×
479
                        return err
×
480
                }
×
481

482
                *v = *route
×
483

×
484
                return nil
×
485
        }
486

487
        return tlv.NewTypeForDecodingErr(val, "routing.mcRoute", l, l)
×
488
}
489

490
// mcHops is a list of mcHop records.
491
type mcHops []*mcHop
492

493
// Record returns a TLV record that can be used to encode/decode a list of
494
// mcHop to/from a TLV stream.
495
func (h *mcHops) Record() tlv.Record {
12✔
496
        recordSize := func() uint64 {
24✔
497
                var (
12✔
498
                        b   bytes.Buffer
12✔
499
                        buf [8]byte
12✔
500
                )
12✔
501
                if err := encodeMCHops(&b, h, &buf); err != nil {
12✔
502
                        panic(err)
×
503
                }
504

505
                return uint64(len(b.Bytes()))
12✔
506
        }
507

508
        return tlv.MakeDynamicRecord(
12✔
509
                0, h, recordSize, encodeMCHops, decodeMCHops,
12✔
510
        )
12✔
511
}
512

513
func encodeMCHops(w io.Writer, val interface{}, buf *[8]byte) error {
24✔
514
        if v, ok := val.(*mcHops); ok {
48✔
515
                // Encode the number of hops as a var int.
24✔
516
                if err := tlv.WriteVarInt(w, uint64(len(*v)), buf); err != nil {
24✔
517
                        return err
×
518
                }
×
519

520
                // With that written out, we'll now encode the entries
521
                // themselves as a sub-TLV record, which includes its _own_
522
                // inner length prefix.
523
                for _, hop := range *v {
72✔
524
                        var hopBytes bytes.Buffer
48✔
525
                        if err := serializeNewHop(&hopBytes, hop); err != nil {
48✔
526
                                return err
×
527
                        }
×
528

529
                        // We encode the record with a varint length followed by
530
                        // the _raw_ TLV bytes.
531
                        tlvLen := uint64(len(hopBytes.Bytes()))
48✔
532
                        if err := tlv.WriteVarInt(w, tlvLen, buf); err != nil {
48✔
533
                                return err
×
534
                        }
×
535

536
                        if _, err := w.Write(hopBytes.Bytes()); err != nil {
48✔
537
                                return err
×
538
                        }
×
539
                }
540

541
                return nil
24✔
542
        }
543

544
        return tlv.NewTypeForEncodingErr(val, "routing.mcHops")
×
545
}
546

547
func decodeMCHops(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
×
548
        if v, ok := val.(*mcHops); ok {
×
549
                // First, we'll decode the varint that encodes how many hops
×
550
                // are encoded in the stream.
×
551
                numHops, err := tlv.ReadVarInt(r, buf)
×
552
                if err != nil {
×
553
                        return err
×
554
                }
×
555

556
                // Now that we know how many records we'll need to read, we can
557
                // iterate and read them all out in series.
558
                for i := uint64(0); i < numHops; i++ {
×
559
                        // Read out the varint that encodes the size of this
×
560
                        // inner TLV record.
×
561
                        hopSize, err := tlv.ReadVarInt(r, buf)
×
562
                        if err != nil {
×
563
                                return err
×
564
                        }
×
565

566
                        // Using this information, we'll create a new limited
567
                        // reader that'll return an EOF once the end has been
568
                        // reached so the stream stops consuming bytes.
569
                        innerTlvReader := &io.LimitedReader{
×
570
                                R: r,
×
571
                                N: int64(hopSize),
×
572
                        }
×
573

×
574
                        hop, err := deserializeNewHop(innerTlvReader)
×
575
                        if err != nil {
×
576
                                return err
×
577
                        }
×
578

579
                        *v = append(*v, hop)
×
580
                }
581

582
                return nil
×
583
        }
584

585
        return tlv.NewTypeForDecodingErr(val, "routing.mcHops", l, l)
×
586
}
587

588
// serializeRoute serializes a mcRoute and writes the resulting bytes to the
589
// given io.Writer.
590
func serializeRoute(w io.Writer, r *mcRoute) error {
12✔
591
        records := lnwire.ProduceRecordsSorted(
12✔
592
                &r.sourcePubKey,
12✔
593
                &r.totalAmount,
12✔
594
                &r.hops,
12✔
595
        )
12✔
596

12✔
597
        return lnwire.EncodeRecordsTo(w, records)
12✔
598
}
12✔
599

600
// deserializeRoute deserializes the mcRoute from the given io.Reader.
601
func deserializeRoute(r io.Reader) (*mcRoute, error) {
×
602
        var rt mcRoute
×
603
        records := lnwire.ProduceRecordsSorted(
×
604
                &rt.sourcePubKey,
×
605
                &rt.totalAmount,
×
606
                &rt.hops,
×
607
        )
×
608

×
609
        _, err := lnwire.DecodeRecords(r, records...)
×
610
        if err != nil {
×
611
                return nil, err
×
612
        }
×
613

614
        return &rt, nil
×
615
}
616

617
// deserializeNewHop deserializes the mcHop from the given io.Reader.
618
func deserializeNewHop(r io.Reader) (*mcHop, error) {
×
619
        var (
×
620
                h        mcHop
×
621
                blinding = tlv.ZeroRecordT[tlv.TlvType3, lnwire.TrueBoolean]()
×
622
                custom   = tlv.ZeroRecordT[tlv.TlvType4, lnwire.TrueBoolean]()
×
623
        )
×
624
        records := lnwire.ProduceRecordsSorted(
×
625
                &h.channelID,
×
626
                &h.pubKeyBytes,
×
627
                &h.amtToFwd,
×
628
                &blinding,
×
629
                &custom,
×
630
        )
×
631

×
632
        typeMap, err := lnwire.DecodeRecords(r, records...)
×
633
        if err != nil {
×
634
                return nil, err
×
635
        }
×
636

637
        if _, ok := typeMap[h.hasBlindingPoint.TlvType()]; ok {
×
638
                h.hasBlindingPoint = tlv.SomeRecordT(blinding)
×
639
        }
×
640

641
        if _, ok := typeMap[h.hasCustomRecords.TlvType()]; ok {
×
642
                h.hasCustomRecords = tlv.SomeRecordT(custom)
×
643
        }
×
644

645
        return &h, nil
×
646
}
647

648
// serializeNewHop serializes a mcHop and writes the resulting bytes to the
649
// given io.Writer.
650
func serializeNewHop(w io.Writer, h *mcHop) error {
48✔
651
        recordProducers := []tlv.RecordProducer{
48✔
652
                &h.channelID,
48✔
653
                &h.pubKeyBytes,
48✔
654
                &h.amtToFwd,
48✔
655
        }
48✔
656

48✔
657
        h.hasBlindingPoint.WhenSome(func(
48✔
658
                hasBlinding tlv.RecordT[tlv.TlvType3, lnwire.TrueBoolean]) {
80✔
659

32✔
660
                recordProducers = append(recordProducers, &hasBlinding)
32✔
661
        })
32✔
662

663
        h.hasCustomRecords.WhenSome(func(
48✔
664
                hasCustom tlv.RecordT[tlv.TlvType4, lnwire.TrueBoolean]) {
80✔
665

32✔
666
                recordProducers = append(recordProducers, &hasCustom)
32✔
667
        })
32✔
668

669
        return lnwire.EncodeRecordsTo(
48✔
670
                w, lnwire.ProduceRecordsSorted(recordProducers...),
48✔
671
        )
48✔
672
}
673

674
// mcHop holds the bare minimum info about a payment attempt route hop that MC
675
// requires.
676
type mcHop struct {
677
        channelID        tlv.RecordT[tlv.TlvType0, uint64]
678
        pubKeyBytes      tlv.RecordT[tlv.TlvType1, Vertex]
679
        amtToFwd         tlv.RecordT[tlv.TlvType2, lnwire.MilliSatoshi]
680
        hasBlindingPoint tlv.OptionalRecordT[tlv.TlvType3, lnwire.TrueBoolean]
681
        hasCustomRecords tlv.OptionalRecordT[tlv.TlvType4, lnwire.TrueBoolean]
682
}
683

684
// serializeOldResult serializes a payment result and returns a key and value
685
// byte slice to insert into the bucket.
686
func serializeOldResult(rp *paymentResultOld) ([]byte, []byte, error) {
3✔
687
        // Write timestamps, success status, failure source index and route.
3✔
688
        var b bytes.Buffer
3✔
689
        var dbFailureSourceIdx int32
3✔
690
        if rp.failureSourceIdx == nil {
4✔
691
                dbFailureSourceIdx = unknownFailureSourceIdx
1✔
692
        } else {
3✔
693
                dbFailureSourceIdx = int32(*rp.failureSourceIdx)
2✔
694
        }
2✔
695
        err := WriteElements(
3✔
696
                &b,
3✔
697
                uint64(rp.timeFwd.UnixNano()),
3✔
698
                uint64(rp.timeReply.UnixNano()),
3✔
699
                rp.success, dbFailureSourceIdx,
3✔
700
        )
3✔
701
        if err != nil {
3✔
702
                return nil, nil, err
×
703
        }
×
704

705
        if err := SerializeRoute(&b, *rp.route); err != nil {
3✔
706
                return nil, nil, err
×
707
        }
×
708

709
        // Write failure. If there is no failure message, write an empty
710
        // byte slice.
711
        var failureBytes bytes.Buffer
3✔
712
        if rp.failure != nil {
4✔
713
                err := lnwire.EncodeFailureMessage(&failureBytes, rp.failure, 0)
1✔
714
                if err != nil {
1✔
715
                        return nil, nil, err
×
716
                }
×
717
        }
718
        err = wire.WriteVarBytes(&b, 0, failureBytes.Bytes())
3✔
719
        if err != nil {
3✔
720
                return nil, nil, err
×
721
        }
×
722
        // Compose key that identifies this result.
723
        key := getResultKeyOld(rp)
3✔
724

3✔
725
        return key, b.Bytes(), nil
3✔
726
}
727

728
// getResultKeyOld returns a byte slice representing a unique key for this
729
// payment result.
730
func getResultKeyOld(rp *paymentResultOld) []byte {
3✔
731
        var keyBytes [8 + 8 + 33]byte
3✔
732

3✔
733
        // Identify records by a combination of time, payment id and sender pub
3✔
734
        // key. This allows importing mission control data from an external
3✔
735
        // source without key collisions and keeps the records sorted
3✔
736
        // chronologically.
3✔
737
        byteOrder.PutUint64(keyBytes[:], uint64(rp.timeReply.UnixNano()))
3✔
738
        byteOrder.PutUint64(keyBytes[8:], rp.id)
3✔
739
        copy(keyBytes[16:], rp.route.SourcePubKey[:])
3✔
740

3✔
741
        return keyBytes[:]
3✔
742
}
3✔
743

744
// serializeNewResult serializes a payment result and returns a key and value
745
// byte slice to insert into the bucket.
746
func serializeNewResult(rp *paymentResultNew) ([]byte, []byte, error) {
6✔
747
        recordProducers := []tlv.RecordProducer{
6✔
748
                &rp.timeFwd,
6✔
749
                &rp.timeReply,
6✔
750
                &rp.route,
6✔
751
        }
6✔
752

6✔
753
        rp.failure.WhenSome(
6✔
754
                func(failure tlv.RecordT[tlv.TlvType3, paymentFailure]) {
10✔
755
                        recordProducers = append(recordProducers, &failure)
4✔
756
                },
4✔
757
        )
758

759
        // Compose key that identifies this result.
760
        key := getResultKeyNew(rp)
6✔
761

6✔
762
        var buff bytes.Buffer
6✔
763
        err := lnwire.EncodeRecordsTo(
6✔
764
                &buff, lnwire.ProduceRecordsSorted(recordProducers...),
6✔
765
        )
6✔
766
        if err != nil {
6✔
767
                return nil, nil, err
×
768
        }
×
769

770
        return key, buff.Bytes(), nil
6✔
771
}
772

773
// getResultKeyNew returns a byte slice representing a unique key for this
774
// payment result.
775
func getResultKeyNew(rp *paymentResultNew) []byte {
6✔
776
        var keyBytes [8 + 8 + 33]byte
6✔
777

6✔
778
        // Identify records by a combination of time, payment id and sender pub
6✔
779
        // key. This allows importing mission control data from an external
6✔
780
        // source without key collisions and keeps the records sorted
6✔
781
        // chronologically.
6✔
782
        byteOrder.PutUint64(keyBytes[:], rp.timeReply.Val)
6✔
783
        byteOrder.PutUint64(keyBytes[8:], rp.id)
6✔
784
        copy(keyBytes[16:], rp.route.Val.sourcePubKey.Val[:])
6✔
785

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