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

lightningnetwork / lnd / 14757563406

30 Apr 2025 02:52PM UTC coverage: 69.041% (+0.009%) from 69.032%
14757563406

Pull #9770

github

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

105 of 118 new or added lines in 3 files covered. (88.98%)

63 existing lines in 16 files now uncovered.

133927 of 193983 relevant lines covered (69.04%)

22092.0 hits per line

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

67.58
/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
// newPaymentFailure constructs a new paymentFailure struct. If the source
149
// index is nil, then an empty paymentFailure is returned. This represents a
150
// failure with unknown details. Otherwise, the index and failure message are
151
// used to populate the info field of the paymentFailure.
152
func newPaymentFailure(sourceIdx *int,
153
        failureMsg lnwire.FailureMessage) paymentFailure {
4✔
154

4✔
155
        // If we can't identify a failure source, we also won't have a decrypted
4✔
156
        // failure message. In this case we return an empty payment failure.
4✔
157
        if sourceIdx == nil {
4✔
NEW
158
                return paymentFailure{}
×
159
        }
×
160

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

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

177
        return info
4✔
178
}
179

180
// paymentFailure holds additional information about a payment failure.
181
type paymentFailure struct {
182
        // sourceIdx is the hop the error was reported from. In order to be able
183
        // to decrypt the error message, we need to know the source, which is
184
        // why an error message can only be present if the source is known.
185
        sourceIdx tlv.OptionalRecordT[tlv.TlvType0, uint8]
186

187
        // msg is the error why a payment failed. If we identify the failure of
188
        // a certain hop at the above index, but aren't able to decode the
189
        // failure message we indicate this by not setting this field.
190
        msg tlv.OptionalRecordT[tlv.TlvType1, failureMessage]
191
}
192

193
// Record returns a TLV record that can be used to encode/decode a
194
// paymentFailure to/from a TLV stream.
195
func (r *paymentFailure) Record() tlv.Record {
4✔
196
        recordSize := func() uint64 {
8✔
197
                var (
4✔
198
                        b   bytes.Buffer
4✔
199
                        buf [8]byte
4✔
200
                )
4✔
201
                if err := encodePaymentFailure(&b, r, &buf); err != nil {
4✔
202
                        panic(err)
×
203
                }
204

205
                return uint64(len(b.Bytes()))
4✔
206
        }
207

208
        return tlv.MakeDynamicRecord(
4✔
209
                0, r, recordSize, encodePaymentFailure, decodePaymentFailure,
4✔
210
        )
4✔
211
}
212

213
func encodePaymentFailure(w io.Writer, val interface{}, _ *[8]byte) error {
8✔
214
        if v, ok := val.(*paymentFailure); ok {
16✔
215
                var recordProducers []tlv.RecordProducer
8✔
216

8✔
217
                v.sourceIdx.WhenSome(
8✔
218
                        func(r tlv.RecordT[tlv.TlvType0, uint8]) {
16✔
219
                                recordProducers = append(
8✔
220
                                        recordProducers, &r,
8✔
221
                                )
8✔
222
                        },
8✔
223
                )
224

225
                v.msg.WhenSome(
8✔
226
                        func(r tlv.RecordT[tlv.TlvType1, failureMessage]) {
12✔
227
                                recordProducers = append(
4✔
228
                                        recordProducers, &r,
4✔
229
                                )
4✔
230
                        },
4✔
231
                )
232

233
                return lnwire.EncodeRecordsTo(
8✔
234
                        w, lnwire.ProduceRecordsSorted(
8✔
235
                                recordProducers...,
8✔
236
                        ),
8✔
237
                )
8✔
238
        }
239

240
        return tlv.NewTypeForEncodingErr(val, "routing.paymentFailure")
×
241
}
242

243
func decodePaymentFailure(r io.Reader, val interface{}, _ *[8]byte,
244
        l uint64) error {
×
245
        if v, ok := val.(*paymentFailure); ok {
×
246
                var h paymentFailure
×
247

×
NEW
248
                sourceIdx := tlv.ZeroRecordT[tlv.TlvType0, uint8]()
×
NEW
249
                msg := tlv.ZeroRecordT[tlv.TlvType1, failureMessage]()
×
NEW
250

×
251
                typeMap, err := lnwire.DecodeRecords(
×
NEW
252
                        r,
×
NEW
253
                        lnwire.ProduceRecordsSorted(&sourceIdx, &msg)...,
×
254
                )
×
NEW
255

×
256
                if err != nil {
×
257
                        return err
×
258
                }
×
259

NEW
260
                if _, ok := typeMap[h.sourceIdx.TlvType()]; ok {
×
NEW
261
                        h.sourceIdx = tlv.SomeRecordT(sourceIdx)
×
UNCOV
262
                }
×
263

NEW
264
                if _, ok := typeMap[h.msg.TlvType()]; ok {
×
NEW
265
                        h.msg = tlv.SomeRecordT(msg)
×
UNCOV
266
                }
×
267

268
                *v = h
×
269

×
270
                return nil
×
271
        }
272

273
        return tlv.NewTypeForDecodingErr(
×
NEW
274
                val, "routing.paymentFailure", l, l,
×
275
        )
×
276
}
277

278
type failureMessage struct {
279
        lnwire.FailureMessage
280
}
281

282
// Record returns a TLV record that can be used to encode/decode a list of
283
// failureMessage to/from a TLV stream.
284
func (r *failureMessage) Record() tlv.Record {
4✔
285
        recordSize := func() uint64 {
8✔
286
                var (
4✔
287
                        b   bytes.Buffer
4✔
288
                        buf [8]byte
4✔
289
                )
4✔
290
                if err := encodeFailureMessage(&b, r, &buf); err != nil {
4✔
291
                        panic(err)
×
292
                }
293

294
                return uint64(len(b.Bytes()))
4✔
295
        }
296

297
        return tlv.MakeDynamicRecord(
4✔
298
                0, r, recordSize, encodeFailureMessage, decodeFailureMessage,
4✔
299
        )
4✔
300
}
301

302
func encodeFailureMessage(w io.Writer, val interface{}, _ *[8]byte) error {
8✔
303
        if v, ok := val.(*failureMessage); ok {
16✔
304
                var b bytes.Buffer
8✔
305
                err := lnwire.EncodeFailureMessage(&b, v.FailureMessage, 0)
8✔
306
                if err != nil {
8✔
307
                        return err
×
308
                }
×
309

310
                _, err = w.Write(b.Bytes())
8✔
311

8✔
312
                return err
8✔
313
        }
314

315
        return tlv.NewTypeForEncodingErr(val, "routing.failureMessage")
×
316
}
317

318
func decodeFailureMessage(r io.Reader, val interface{}, _ *[8]byte,
319
        l uint64) error {
×
320

×
321
        if v, ok := val.(*failureMessage); ok {
×
322
                msg, err := lnwire.DecodeFailureMessage(r, 0)
×
323
                if err != nil {
×
324
                        return err
×
325
                }
×
326

327
                *v = failureMessage{
×
328
                        FailureMessage: msg,
×
329
                }
×
330

×
331
                return nil
×
332
        }
333

334
        return tlv.NewTypeForDecodingErr(val, "routing.failureMessage", l, l)
×
335
}
336

337
// extractMCRoute extracts the fields required by MC from the Route struct to
338
// create the more minimal mcRoute struct.
339
func extractMCRoute(r *Route) *mcRoute {
3✔
340
        return &mcRoute{
3✔
341
                sourcePubKey: tlv.NewRecordT[tlv.TlvType0](r.SourcePubKey),
3✔
342
                totalAmount:  tlv.NewRecordT[tlv.TlvType1](r.TotalAmount),
3✔
343
                hops: tlv.NewRecordT[tlv.TlvType2](
3✔
344
                        extractMCHops(r.Hops),
3✔
345
                ),
3✔
346
        }
3✔
347
}
3✔
348

349
// extractMCHops extracts the Hop fields that MC actually uses from a slice of
350
// Hops.
351
func extractMCHops(hops []*Hop) mcHops {
3✔
352
        return fn.Map(hops, extractMCHop)
3✔
353
}
3✔
354

355
// extractMCHop extracts the Hop fields that MC actually uses from a Hop.
356
func extractMCHop(hop *Hop) *mcHop {
6✔
357
        h := mcHop{
6✔
358
                channelID: tlv.NewPrimitiveRecord[tlv.TlvType0, uint64](
6✔
359
                        hop.ChannelID,
6✔
360
                ),
6✔
361
                pubKeyBytes: tlv.NewRecordT[tlv.TlvType1, Vertex](
6✔
362
                        hop.PubKeyBytes,
6✔
363
                ),
6✔
364
                amtToFwd: tlv.NewRecordT[tlv.TlvType2, lnwire.MilliSatoshi](
6✔
365
                        hop.AmtToForward,
6✔
366
                ),
6✔
367
        }
6✔
368

6✔
369
        if hop.BlindingPoint != nil {
10✔
370
                h.hasBlindingPoint = tlv.SomeRecordT(
4✔
371
                        tlv.NewRecordT[tlv.TlvType3, lnwire.TrueBoolean](
4✔
372
                                lnwire.TrueBoolean{},
4✔
373
                        ),
4✔
374
                )
4✔
375
        }
4✔
376

377
        if len(hop.CustomRecords) != 0 {
10✔
378
                h.hasCustomRecords = tlv.SomeRecordT(
4✔
379
                        tlv.NewRecordT[tlv.TlvType4, lnwire.TrueBoolean](
4✔
380
                                lnwire.TrueBoolean{},
4✔
381
                        ),
4✔
382
                )
4✔
383
        }
4✔
384

385
        return &h
6✔
386
}
387

388
// mcRoute holds the bare minimum info about a payment attempt route that MC
389
// requires.
390
type mcRoute struct {
391
        sourcePubKey tlv.RecordT[tlv.TlvType0, Vertex]
392
        totalAmount  tlv.RecordT[tlv.TlvType1, lnwire.MilliSatoshi]
393
        hops         tlv.RecordT[tlv.TlvType2, mcHops]
394
}
395

396
// Record returns a TLV record that can be used to encode/decode an mcRoute
397
// to/from a TLV stream.
398
func (r *mcRoute) Record() tlv.Record {
6✔
399
        recordSize := func() uint64 {
12✔
400
                var (
6✔
401
                        b   bytes.Buffer
6✔
402
                        buf [8]byte
6✔
403
                )
6✔
404
                if err := encodeMCRoute(&b, r, &buf); err != nil {
6✔
405
                        panic(err)
×
406
                }
407

408
                return uint64(len(b.Bytes()))
6✔
409
        }
410

411
        return tlv.MakeDynamicRecord(
6✔
412
                0, r, recordSize, encodeMCRoute, decodeMCRoute,
6✔
413
        )
6✔
414
}
415

416
func encodeMCRoute(w io.Writer, val interface{}, _ *[8]byte) error {
12✔
417
        if v, ok := val.(*mcRoute); ok {
24✔
418
                return serializeRoute(w, v)
12✔
419
        }
12✔
420

421
        return tlv.NewTypeForEncodingErr(val, "routing.mcRoute")
×
422
}
423

424
func decodeMCRoute(r io.Reader, val interface{}, _ *[8]byte, l uint64) error {
×
425
        if v, ok := val.(*mcRoute); ok {
×
426
                route, err := deserializeRoute(io.LimitReader(r, int64(l)))
×
427
                if err != nil {
×
428
                        return err
×
429
                }
×
430

431
                *v = *route
×
432

×
433
                return nil
×
434
        }
435

436
        return tlv.NewTypeForDecodingErr(val, "routing.mcRoute", l, l)
×
437
}
438

439
// mcHops is a list of mcHop records.
440
type mcHops []*mcHop
441

442
// Record returns a TLV record that can be used to encode/decode a list of
443
// mcHop to/from a TLV stream.
444
func (h *mcHops) Record() tlv.Record {
12✔
445
        recordSize := func() uint64 {
24✔
446
                var (
12✔
447
                        b   bytes.Buffer
12✔
448
                        buf [8]byte
12✔
449
                )
12✔
450
                if err := encodeMCHops(&b, h, &buf); err != nil {
12✔
451
                        panic(err)
×
452
                }
453

454
                return uint64(len(b.Bytes()))
12✔
455
        }
456

457
        return tlv.MakeDynamicRecord(
12✔
458
                0, h, recordSize, encodeMCHops, decodeMCHops,
12✔
459
        )
12✔
460
}
461

462
func encodeMCHops(w io.Writer, val interface{}, buf *[8]byte) error {
24✔
463
        if v, ok := val.(*mcHops); ok {
48✔
464
                // Encode the number of hops as a var int.
24✔
465
                if err := tlv.WriteVarInt(w, uint64(len(*v)), buf); err != nil {
24✔
466
                        return err
×
467
                }
×
468

469
                // With that written out, we'll now encode the entries
470
                // themselves as a sub-TLV record, which includes its _own_
471
                // inner length prefix.
472
                for _, hop := range *v {
72✔
473
                        var hopBytes bytes.Buffer
48✔
474
                        if err := serializeNewHop(&hopBytes, hop); err != nil {
48✔
475
                                return err
×
476
                        }
×
477

478
                        // We encode the record with a varint length followed by
479
                        // the _raw_ TLV bytes.
480
                        tlvLen := uint64(len(hopBytes.Bytes()))
48✔
481
                        if err := tlv.WriteVarInt(w, tlvLen, buf); err != nil {
48✔
482
                                return err
×
483
                        }
×
484

485
                        if _, err := w.Write(hopBytes.Bytes()); err != nil {
48✔
486
                                return err
×
487
                        }
×
488
                }
489

490
                return nil
24✔
491
        }
492

493
        return tlv.NewTypeForEncodingErr(val, "routing.mcHops")
×
494
}
495

496
func decodeMCHops(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
×
497
        if v, ok := val.(*mcHops); ok {
×
498
                // First, we'll decode the varint that encodes how many hops
×
499
                // are encoded in the stream.
×
500
                numHops, err := tlv.ReadVarInt(r, buf)
×
501
                if err != nil {
×
502
                        return err
×
503
                }
×
504

505
                // Now that we know how many records we'll need to read, we can
506
                // iterate and read them all out in series.
507
                for i := uint64(0); i < numHops; i++ {
×
508
                        // Read out the varint that encodes the size of this
×
509
                        // inner TLV record.
×
510
                        hopSize, err := tlv.ReadVarInt(r, buf)
×
511
                        if err != nil {
×
512
                                return err
×
513
                        }
×
514

515
                        // Using this information, we'll create a new limited
516
                        // reader that'll return an EOF once the end has been
517
                        // reached so the stream stops consuming bytes.
518
                        innerTlvReader := &io.LimitedReader{
×
519
                                R: r,
×
520
                                N: int64(hopSize),
×
521
                        }
×
522

×
523
                        hop, err := deserializeNewHop(innerTlvReader)
×
524
                        if err != nil {
×
525
                                return err
×
526
                        }
×
527

528
                        *v = append(*v, hop)
×
529
                }
530

531
                return nil
×
532
        }
533

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

537
// serializeRoute serializes a mcRoute and writes the resulting bytes to the
538
// given io.Writer.
539
func serializeRoute(w io.Writer, r *mcRoute) error {
12✔
540
        records := lnwire.ProduceRecordsSorted(
12✔
541
                &r.sourcePubKey,
12✔
542
                &r.totalAmount,
12✔
543
                &r.hops,
12✔
544
        )
12✔
545

12✔
546
        return lnwire.EncodeRecordsTo(w, records)
12✔
547
}
12✔
548

549
// deserializeRoute deserializes the mcRoute from the given io.Reader.
550
func deserializeRoute(r io.Reader) (*mcRoute, error) {
×
551
        var rt mcRoute
×
552
        records := lnwire.ProduceRecordsSorted(
×
553
                &rt.sourcePubKey,
×
554
                &rt.totalAmount,
×
555
                &rt.hops,
×
556
        )
×
557

×
558
        _, err := lnwire.DecodeRecords(r, records...)
×
559
        if err != nil {
×
560
                return nil, err
×
561
        }
×
562

563
        return &rt, nil
×
564
}
565

566
// deserializeNewHop deserializes the mcHop from the given io.Reader.
567
func deserializeNewHop(r io.Reader) (*mcHop, error) {
×
568
        var (
×
569
                h        mcHop
×
570
                blinding = tlv.ZeroRecordT[tlv.TlvType3, lnwire.TrueBoolean]()
×
571
                custom   = tlv.ZeroRecordT[tlv.TlvType4, lnwire.TrueBoolean]()
×
572
        )
×
573
        records := lnwire.ProduceRecordsSorted(
×
574
                &h.channelID,
×
575
                &h.pubKeyBytes,
×
576
                &h.amtToFwd,
×
577
                &blinding,
×
578
                &custom,
×
579
        )
×
580

×
581
        typeMap, err := lnwire.DecodeRecords(r, records...)
×
582
        if err != nil {
×
583
                return nil, err
×
584
        }
×
585

586
        if _, ok := typeMap[h.hasBlindingPoint.TlvType()]; ok {
×
587
                h.hasBlindingPoint = tlv.SomeRecordT(blinding)
×
588
        }
×
589

590
        if _, ok := typeMap[h.hasCustomRecords.TlvType()]; ok {
×
591
                h.hasCustomRecords = tlv.SomeRecordT(custom)
×
592
        }
×
593

594
        return &h, nil
×
595
}
596

597
// serializeNewHop serializes a mcHop and writes the resulting bytes to the
598
// given io.Writer.
599
func serializeNewHop(w io.Writer, h *mcHop) error {
48✔
600
        recordProducers := []tlv.RecordProducer{
48✔
601
                &h.channelID,
48✔
602
                &h.pubKeyBytes,
48✔
603
                &h.amtToFwd,
48✔
604
        }
48✔
605

48✔
606
        h.hasBlindingPoint.WhenSome(func(
48✔
607
                hasBlinding tlv.RecordT[tlv.TlvType3, lnwire.TrueBoolean]) {
80✔
608

32✔
609
                recordProducers = append(recordProducers, &hasBlinding)
32✔
610
        })
32✔
611

612
        h.hasCustomRecords.WhenSome(func(
48✔
613
                hasCustom tlv.RecordT[tlv.TlvType4, lnwire.TrueBoolean]) {
80✔
614

32✔
615
                recordProducers = append(recordProducers, &hasCustom)
32✔
616
        })
32✔
617

618
        return lnwire.EncodeRecordsTo(
48✔
619
                w, lnwire.ProduceRecordsSorted(recordProducers...),
48✔
620
        )
48✔
621
}
622

623
// mcHop holds the bare minimum info about a payment attempt route hop that MC
624
// requires.
625
type mcHop struct {
626
        channelID        tlv.RecordT[tlv.TlvType0, uint64]
627
        pubKeyBytes      tlv.RecordT[tlv.TlvType1, Vertex]
628
        amtToFwd         tlv.RecordT[tlv.TlvType2, lnwire.MilliSatoshi]
629
        hasBlindingPoint tlv.OptionalRecordT[tlv.TlvType3, lnwire.TrueBoolean]
630
        hasCustomRecords tlv.OptionalRecordT[tlv.TlvType4, lnwire.TrueBoolean]
631
}
632

633
// serializeOldResult serializes a payment result and returns a key and value
634
// byte slice to insert into the bucket.
635
func serializeOldResult(rp *paymentResultOld) ([]byte, []byte, error) {
3✔
636
        // Write timestamps, success status, failure source index and route.
3✔
637
        var b bytes.Buffer
3✔
638
        var dbFailureSourceIdx int32
3✔
639
        if rp.failureSourceIdx == nil {
4✔
640
                dbFailureSourceIdx = unknownFailureSourceIdx
1✔
641
        } else {
3✔
642
                dbFailureSourceIdx = int32(*rp.failureSourceIdx)
2✔
643
        }
2✔
644
        err := WriteElements(
3✔
645
                &b,
3✔
646
                uint64(rp.timeFwd.UnixNano()),
3✔
647
                uint64(rp.timeReply.UnixNano()),
3✔
648
                rp.success, dbFailureSourceIdx,
3✔
649
        )
3✔
650
        if err != nil {
3✔
651
                return nil, nil, err
×
652
        }
×
653

654
        if err := SerializeRoute(&b, *rp.route); err != nil {
3✔
655
                return nil, nil, err
×
656
        }
×
657

658
        // Write failure. If there is no failure message, write an empty
659
        // byte slice.
660
        var failureBytes bytes.Buffer
3✔
661
        if rp.failure != nil {
4✔
662
                err := lnwire.EncodeFailureMessage(&failureBytes, rp.failure, 0)
1✔
663
                if err != nil {
1✔
664
                        return nil, nil, err
×
665
                }
×
666
        }
667
        err = wire.WriteVarBytes(&b, 0, failureBytes.Bytes())
3✔
668
        if err != nil {
3✔
669
                return nil, nil, err
×
670
        }
×
671
        // Compose key that identifies this result.
672
        key := getResultKeyOld(rp)
3✔
673

3✔
674
        return key, b.Bytes(), nil
3✔
675
}
676

677
// getResultKeyOld returns a byte slice representing a unique key for this
678
// payment result.
679
func getResultKeyOld(rp *paymentResultOld) []byte {
3✔
680
        var keyBytes [8 + 8 + 33]byte
3✔
681

3✔
682
        // Identify records by a combination of time, payment id and sender pub
3✔
683
        // key. This allows importing mission control data from an external
3✔
684
        // source without key collisions and keeps the records sorted
3✔
685
        // chronologically.
3✔
686
        byteOrder.PutUint64(keyBytes[:], uint64(rp.timeReply.UnixNano()))
3✔
687
        byteOrder.PutUint64(keyBytes[8:], rp.id)
3✔
688
        copy(keyBytes[16:], rp.route.SourcePubKey[:])
3✔
689

3✔
690
        return keyBytes[:]
3✔
691
}
3✔
692

693
// serializeNewResult serializes a payment result and returns a key and value
694
// byte slice to insert into the bucket.
695
func serializeNewResult(rp *paymentResultNew) ([]byte, []byte, error) {
6✔
696
        recordProducers := []tlv.RecordProducer{
6✔
697
                &rp.timeFwd,
6✔
698
                &rp.timeReply,
6✔
699
                &rp.route,
6✔
700
        }
6✔
701

6✔
702
        rp.failure.WhenSome(
6✔
703
                func(failure tlv.RecordT[tlv.TlvType3, paymentFailure]) {
10✔
704
                        recordProducers = append(recordProducers, &failure)
4✔
705
                },
4✔
706
        )
707

708
        // Compose key that identifies this result.
709
        key := getResultKeyNew(rp)
6✔
710

6✔
711
        var buff bytes.Buffer
6✔
712
        err := lnwire.EncodeRecordsTo(
6✔
713
                &buff, lnwire.ProduceRecordsSorted(recordProducers...),
6✔
714
        )
6✔
715
        if err != nil {
6✔
716
                return nil, nil, err
×
717
        }
×
718

719
        return key, buff.Bytes(), nil
6✔
720
}
721

722
// getResultKeyNew returns a byte slice representing a unique key for this
723
// payment result.
724
func getResultKeyNew(rp *paymentResultNew) []byte {
6✔
725
        var keyBytes [8 + 8 + 33]byte
6✔
726

6✔
727
        // Identify records by a combination of time, payment id and sender pub
6✔
728
        // key. This allows importing mission control data from an external
6✔
729
        // source without key collisions and keeps the records sorted
6✔
730
        // chronologically.
6✔
731
        byteOrder.PutUint64(keyBytes[:], rp.timeReply.Val)
6✔
732
        byteOrder.PutUint64(keyBytes[8:], rp.id)
6✔
733
        copy(keyBytes[16:], rp.route.Val.sourcePubKey.Val[:])
6✔
734

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