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

lightningnetwork / lnd / 14880107727

07 May 2025 09:35AM UTC coverage: 58.446% (-10.5%) from 68.992%
14880107727

Pull #9789

github

web-flow
Merge ed3471042 into 67a40c90a
Pull Request #9789: multi: use updated TLV SizeFunc signature

54 of 95 new or added lines in 19 files covered. (56.84%)

28462 existing lines in 449 files now uncovered.

97165 of 166247 relevant lines covered (58.45%)

1.81 hits per line

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

0.0
/channeldb/migration32/route.go
1
package migration32
2

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

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

15
const (
16
        // MPPOnionType is the type used in the onion to reference the MPP
17
        // fields: total_amt and payment_addr.
18
        MPPOnionType tlv.Type = 8
19

20
        // AMPOnionType is the type used in the onion to reference the AMP
21
        // fields: root_share, set_id, and child_index.
22
        AMPOnionType tlv.Type = 14
23
)
24

25
// VertexSize is the size of the array to store a vertex.
26
const VertexSize = 33
27

28
// Vertex is a simple alias for the serialization of a compressed Bitcoin
29
// public key.
30
type Vertex [VertexSize]byte
31

32
// Record returns a TLV record that can be used to encode/decode a Vertex
33
// to/from a TLV stream.
UNCOV
34
func (v *Vertex) Record() tlv.Record {
×
UNCOV
35
        return tlv.MakeStaticRecord(
×
UNCOV
36
                0, v, VertexSize, encodeVertex, decodeVertex,
×
UNCOV
37
        )
×
UNCOV
38
}
×
39

UNCOV
40
func encodeVertex(w io.Writer, val interface{}, _ *[8]byte) error {
×
UNCOV
41
        if b, ok := val.(*Vertex); ok {
×
UNCOV
42
                _, err := w.Write(b[:])
×
UNCOV
43
                return err
×
UNCOV
44
        }
×
45

46
        return tlv.NewTypeForEncodingErr(val, "Vertex")
×
47
}
48

49
func decodeVertex(r io.Reader, val interface{}, _ *[8]byte, l uint64) error {
×
50
        if b, ok := val.(*Vertex); ok {
×
51
                _, err := io.ReadFull(r, b[:])
×
52
                return err
×
53
        }
×
54

55
        return tlv.NewTypeForDecodingErr(val, "Vertex", l, VertexSize)
×
56
}
57

58
// Route represents a path through the channel graph which runs over one or
59
// more channels in succession. This struct carries all the information
60
// required to craft the Sphinx onion packet, and send the payment along the
61
// first hop in the path. A route is only selected as valid if all the channels
62
// have sufficient capacity to carry the initial payment amount after fees are
63
// accounted for.
64
type Route struct {
65
        // TotalTimeLock is the cumulative (final) time lock across the entire
66
        // route. This is the CLTV value that should be extended to the first
67
        // hop in the route. All other hops will decrement the time-lock as
68
        // advertised, leaving enough time for all hops to wait for or present
69
        // the payment preimage to complete the payment.
70
        TotalTimeLock uint32
71

72
        // TotalAmount is the total amount of funds required to complete a
73
        // payment over this route. This value includes the cumulative fees at
74
        // each hop. As a result, the HTLC extended to the first-hop in the
75
        // route will need to have at least this many satoshis, otherwise the
76
        // route will fail at an intermediate node due to an insufficient
77
        // amount of fees.
78
        TotalAmount lnwire.MilliSatoshi
79

80
        // SourcePubKey is the pubkey of the node where this route originates
81
        // from.
82
        SourcePubKey Vertex
83

84
        // Hops contains details concerning the specific forwarding details at
85
        // each hop.
86
        Hops []*Hop
87

88
        // FirstHopAmount is the amount that should actually be sent to the
89
        // first hop in the route. This is only different from TotalAmount above
90
        // for custom channels where the on-chain amount doesn't necessarily
91
        // reflect all the value of an outgoing payment.
92
        FirstHopAmount tlv.RecordT[
93
                tlv.TlvType0, tlv.BigSizeT[lnwire.MilliSatoshi],
94
        ]
95

96
        // FirstHopWireCustomRecords is a set of custom records that should be
97
        // included in the wire message sent to the first hop. This is only set
98
        // on custom channels and is used to include additional information
99
        // about the actual value of the payment.
100
        //
101
        // NOTE: Since these records already represent TLV records, and we
102
        // enforce them to be in the custom range (e.g. >= 65536), we don't use
103
        // another parent record type here. Instead, when serializing the Route
104
        // we merge the TLV records together with the custom records and encode
105
        // everything as a single TLV stream.
106
        FirstHopWireCustomRecords lnwire.CustomRecords
107
}
108

109
// Hop represents an intermediate or final node of the route. This naming
110
// is in line with the definition given in BOLT #4: Onion Routing Protocol.
111
// The struct houses the channel along which this hop can be reached and
112
// the values necessary to create the HTLC that needs to be sent to the
113
// next hop. It is also used to encode the per-hop payload included within
114
// the Sphinx packet.
115
type Hop struct {
116
        // PubKeyBytes is the raw bytes of the public key of the target node.
117
        PubKeyBytes Vertex
118

119
        // ChannelID is the unique channel ID for the channel. The first 3
120
        // bytes are the block height, the next 3 the index within the block,
121
        // and the last 2 bytes are the output index for the channel.
122
        ChannelID uint64
123

124
        // OutgoingTimeLock is the timelock value that should be used when
125
        // crafting the _outgoing_ HTLC from this hop.
126
        OutgoingTimeLock uint32
127

128
        // AmtToForward is the amount that this hop will forward to the next
129
        // hop. This value is less than the value that the incoming HTLC
130
        // carries as a fee will be subtracted by the hop.
131
        AmtToForward lnwire.MilliSatoshi
132

133
        // MPP encapsulates the data required for option_mpp. This field should
134
        // only be set for the final hop.
135
        MPP *MPP
136

137
        // AMP encapsulates the data required for option_amp. This field should
138
        // only be set for the final hop.
139
        AMP *AMP
140

141
        // CustomRecords if non-nil are a set of additional TLV records that
142
        // should be included in the forwarding instructions for this node.
143
        CustomRecords lnwire.CustomRecords
144

145
        // LegacyPayload if true, then this signals that this node doesn't
146
        // understand the new TLV payload, so we must instead use the legacy
147
        // payload.
148
        //
149
        // NOTE: we should no longer ever create a Hop with Legacy set to true.
150
        // The only reason we are keeping this member is that it could be the
151
        // case that we have serialised hops persisted to disk where
152
        // LegacyPayload is true.
153
        LegacyPayload bool
154

155
        // Metadata is additional data that is sent along with the payment to
156
        // the payee.
157
        Metadata []byte
158

159
        // EncryptedData is an encrypted data blob includes for hops that are
160
        // part of a blinded route.
161
        EncryptedData []byte
162

163
        // BlindingPoint is an ephemeral public key used by introduction nodes
164
        // in blinded routes to unblind their portion of the route and pass on
165
        // the next ephemeral key to the next blinded node to do the same.
166
        BlindingPoint *btcec.PublicKey
167

168
        // TotalAmtMsat is the total amount for a blinded payment, potentially
169
        // spread over more than one HTLC. This field should only be set for
170
        // the final hop in a blinded path.
171
        TotalAmtMsat lnwire.MilliSatoshi
172
}
173

174
// MPP is a record that encodes the fields necessary for multi-path payments.
175
type MPP struct {
176
        // paymentAddr is a random, receiver-generated value used to avoid
177
        // collisions with concurrent payers.
178
        paymentAddr [32]byte
179

180
        // totalMsat is the total value of the payment, potentially spread
181
        // across more than one HTLC.
182
        totalMsat lnwire.MilliSatoshi
183
}
184

185
// Record returns a tlv.Record that can be used to encode or decode this record.
UNCOV
186
func (r *MPP) Record() tlv.Record {
×
UNCOV
187
        // Fixed-size, 32 byte payment address followed by truncated 64-bit
×
UNCOV
188
        // total msat.
×
NEW
189
        size := func() (uint64, error) {
×
NEW
190
                return 32 + tlv.SizeTUint64(uint64(r.totalMsat)), nil
×
UNCOV
191
        }
×
192

UNCOV
193
        return tlv.MakeDynamicRecord(
×
UNCOV
194
                MPPOnionType, r, size, MPPEncoder, MPPDecoder,
×
UNCOV
195
        )
×
196
}
197

198
const (
199
        // minMPPLength is the minimum length of a serialized MPP TLV record,
200
        // which occurs when the truncated encoding of total_amt_msat takes 0
201
        // bytes, leaving only the payment_addr.
202
        minMPPLength = 32
203

204
        // maxMPPLength is the maximum length of a serialized MPP TLV record,
205
        // which occurs when the truncated encoding of total_amt_msat takes 8
206
        // bytes.
207
        maxMPPLength = 40
208
)
209

210
// MPPEncoder writes the MPP record to the provided io.Writer.
UNCOV
211
func MPPEncoder(w io.Writer, val interface{}, buf *[8]byte) error {
×
UNCOV
212
        if v, ok := val.(*MPP); ok {
×
UNCOV
213
                err := tlv.EBytes32(w, &v.paymentAddr, buf)
×
UNCOV
214
                if err != nil {
×
215
                        return err
×
216
                }
×
217

UNCOV
218
                return tlv.ETUint64T(w, uint64(v.totalMsat), buf)
×
219
        }
220

221
        return tlv.NewTypeForEncodingErr(val, "MPP")
×
222
}
223

224
// MPPDecoder reads the MPP record to the provided io.Reader.
UNCOV
225
func MPPDecoder(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
×
UNCOV
226
        if v, ok := val.(*MPP); ok && minMPPLength <= l && l <= maxMPPLength {
×
UNCOV
227
                if err := tlv.DBytes32(r, &v.paymentAddr, buf, 32); err != nil {
×
228
                        return err
×
229
                }
×
230

UNCOV
231
                var total uint64
×
UNCOV
232
                if err := tlv.DTUint64(r, &total, buf, l-32); err != nil {
×
233
                        return err
×
234
                }
×
UNCOV
235
                v.totalMsat = lnwire.MilliSatoshi(total)
×
UNCOV
236

×
UNCOV
237
                return nil
×
238
        }
239

240
        return tlv.NewTypeForDecodingErr(val, "MPP", l, maxMPPLength)
×
241
}
242

243
// AMP is a record that encodes the fields necessary for atomic multi-path
244
// payments.
245
type AMP struct {
246
        rootShare  [32]byte
247
        setID      [32]byte
248
        childIndex uint32
249
}
250

251
// AMPEncoder writes the AMP record to the provided io.Writer.
UNCOV
252
func AMPEncoder(w io.Writer, val interface{}, buf *[8]byte) error {
×
UNCOV
253
        if v, ok := val.(*AMP); ok {
×
UNCOV
254
                if err := tlv.EBytes32(w, &v.rootShare, buf); err != nil {
×
255
                        return err
×
256
                }
×
257

UNCOV
258
                if err := tlv.EBytes32(w, &v.setID, buf); err != nil {
×
259
                        return err
×
260
                }
×
261

UNCOV
262
                return tlv.ETUint32T(w, v.childIndex, buf)
×
263
        }
264

265
        return tlv.NewTypeForEncodingErr(val, "AMP")
×
266
}
267

268
const (
269
        // minAMPLength is the minimum length of a serialized AMP TLV record,
270
        // which occurs when the truncated encoding of child_index takes 0
271
        // bytes, leaving only the root_share and set_id.
272
        minAMPLength = 64
273

274
        // maxAMPLength is the maximum length of a serialized AMP TLV record,
275
        // which occurs when the truncated encoding of a child_index takes 2
276
        // bytes.
277
        maxAMPLength = 68
278
)
279

280
// AMPDecoder reads the AMP record from the provided io.Reader.
UNCOV
281
func AMPDecoder(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
×
UNCOV
282
        if v, ok := val.(*AMP); ok && minAMPLength <= l && l <= maxAMPLength {
×
UNCOV
283
                if err := tlv.DBytes32(r, &v.rootShare, buf, 32); err != nil {
×
284
                        return err
×
285
                }
×
286

UNCOV
287
                if err := tlv.DBytes32(r, &v.setID, buf, 32); err != nil {
×
288
                        return err
×
289
                }
×
290

UNCOV
291
                return tlv.DTUint32(r, &v.childIndex, buf, l-minAMPLength)
×
292
        }
293

294
        return tlv.NewTypeForDecodingErr(val, "AMP", l, maxAMPLength)
×
295
}
296

297
// Record returns a tlv.Record that can be used to encode or decode this record.
UNCOV
298
func (a *AMP) Record() tlv.Record {
×
UNCOV
299
        return tlv.MakeDynamicRecord(
×
NEW
300
                AMPOnionType, a, func() (uint64, error) {
×
NEW
301
                        return a.PayloadSize(), nil
×
NEW
302
                }, AMPEncoder, AMPDecoder,
×
303
        )
304
}
305

306
// PayloadSize returns the size this record takes up in encoded form.
307
func (a *AMP) PayloadSize() uint64 {
×
308
        return 32 + 32 + tlv.SizeTUint32(a.childIndex)
×
309
}
×
310

311
// SerializeRoute serializes a route.
UNCOV
312
func SerializeRoute(w io.Writer, r Route) error {
×
UNCOV
313
        if err := WriteElements(w,
×
UNCOV
314
                r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
×
UNCOV
315
        ); err != nil {
×
316
                return err
×
317
        }
×
318

UNCOV
319
        if err := WriteElements(w, uint32(len(r.Hops))); err != nil {
×
320
                return err
×
321
        }
×
322

UNCOV
323
        for _, h := range r.Hops {
×
UNCOV
324
                if err := serializeHop(w, h); err != nil {
×
325
                        return err
×
326
                }
×
327
        }
328

329
        // Any new/extra TLV data is encoded in serializeHTLCAttemptInfo!
330

UNCOV
331
        return nil
×
332
}
333

UNCOV
334
func serializeHop(w io.Writer, h *Hop) error {
×
UNCOV
335
        if err := WriteElements(w,
×
UNCOV
336
                h.PubKeyBytes[:],
×
UNCOV
337
                h.ChannelID,
×
UNCOV
338
                h.OutgoingTimeLock,
×
UNCOV
339
                h.AmtToForward,
×
UNCOV
340
        ); err != nil {
×
341
                return err
×
342
        }
×
343

UNCOV
344
        if err := binary.Write(w, byteOrder, h.LegacyPayload); err != nil {
×
345
                return err
×
346
        }
×
347

348
        // For legacy payloads, we don't need to write any TLV records, so
349
        // we'll write a zero indicating the our serialized TLV map has no
350
        // records.
UNCOV
351
        if h.LegacyPayload {
×
UNCOV
352
                return WriteElements(w, uint32(0))
×
UNCOV
353
        }
×
354

355
        // Gather all non-primitive TLV records so that they can be serialized
356
        // as a single blob.
357
        //
358
        // TODO(conner): add migration to unify all fields in a single TLV
359
        // blobs. The split approach will cause headaches down the road as more
360
        // fields are added, which we can avoid by having a single TLV stream
361
        // for all payload fields.
UNCOV
362
        var records []tlv.Record
×
UNCOV
363
        if h.MPP != nil {
×
UNCOV
364
                records = append(records, h.MPP.Record())
×
UNCOV
365
        }
×
366

367
        // Add blinding point and encrypted data if present.
UNCOV
368
        if h.EncryptedData != nil {
×
UNCOV
369
                records = append(records, NewEncryptedDataRecord(
×
UNCOV
370
                        &h.EncryptedData,
×
UNCOV
371
                ))
×
UNCOV
372
        }
×
373

UNCOV
374
        if h.BlindingPoint != nil {
×
UNCOV
375
                records = append(records, NewBlindingPointRecord(
×
UNCOV
376
                        &h.BlindingPoint,
×
UNCOV
377
                ))
×
UNCOV
378
        }
×
379

UNCOV
380
        if h.AMP != nil {
×
UNCOV
381
                records = append(records, h.AMP.Record())
×
UNCOV
382
        }
×
383

UNCOV
384
        if h.Metadata != nil {
×
UNCOV
385
                records = append(records, NewMetadataRecord(&h.Metadata))
×
UNCOV
386
        }
×
387

UNCOV
388
        if h.TotalAmtMsat != 0 {
×
UNCOV
389
                totalMsatInt := uint64(h.TotalAmtMsat)
×
UNCOV
390
                records = append(
×
UNCOV
391
                        records, NewTotalAmtMsatBlinded(&totalMsatInt),
×
UNCOV
392
                )
×
UNCOV
393
        }
×
394

395
        // Final sanity check to absolutely rule out custom records that are not
396
        // custom and write into the standard range.
UNCOV
397
        if err := h.CustomRecords.Validate(); err != nil {
×
398
                return err
×
399
        }
×
400

401
        // Convert custom records to tlv and add to the record list.
402
        // MapToRecords sorts the list, so adding it here will keep the list
403
        // canonical.
UNCOV
404
        tlvRecords := tlv.MapToRecords(h.CustomRecords)
×
UNCOV
405
        records = append(records, tlvRecords...)
×
UNCOV
406

×
UNCOV
407
        // Otherwise, we'll transform our slice of records into a map of the
×
UNCOV
408
        // raw bytes, then serialize them in-line with a length (number of
×
UNCOV
409
        // elements) prefix.
×
UNCOV
410
        mapRecords, err := tlv.RecordsToMap(records)
×
UNCOV
411
        if err != nil {
×
412
                return err
×
413
        }
×
414

UNCOV
415
        numRecords := uint32(len(mapRecords))
×
UNCOV
416
        if err := WriteElements(w, numRecords); err != nil {
×
417
                return err
×
418
        }
×
419

UNCOV
420
        for recordType, rawBytes := range mapRecords {
×
UNCOV
421
                if err := WriteElements(w, recordType); err != nil {
×
422
                        return err
×
423
                }
×
424

UNCOV
425
                if err := wire.WriteVarBytes(w, 0, rawBytes); err != nil {
×
426
                        return err
×
427
                }
×
428
        }
429

UNCOV
430
        return nil
×
431
}
432

433
// DeserializeRoute deserializes a route.
UNCOV
434
func DeserializeRoute(r io.Reader) (Route, error) {
×
UNCOV
435
        rt := Route{}
×
UNCOV
436
        if err := ReadElements(r,
×
UNCOV
437
                &rt.TotalTimeLock, &rt.TotalAmount,
×
UNCOV
438
        ); err != nil {
×
439
                return rt, err
×
440
        }
×
441

UNCOV
442
        var pub []byte
×
UNCOV
443
        if err := ReadElements(r, &pub); err != nil {
×
444
                return rt, err
×
445
        }
×
UNCOV
446
        copy(rt.SourcePubKey[:], pub)
×
UNCOV
447

×
UNCOV
448
        var numHops uint32
×
UNCOV
449
        if err := ReadElements(r, &numHops); err != nil {
×
450
                return rt, err
×
451
        }
×
452

UNCOV
453
        var hops []*Hop
×
UNCOV
454
        for i := uint32(0); i < numHops; i++ {
×
UNCOV
455
                hop, err := deserializeHop(r)
×
UNCOV
456
                if err != nil {
×
457
                        return rt, err
×
458
                }
×
UNCOV
459
                hops = append(hops, hop)
×
460
        }
UNCOV
461
        rt.Hops = hops
×
UNCOV
462

×
UNCOV
463
        // Any new/extra TLV data is decoded in deserializeHTLCAttemptInfo!
×
UNCOV
464

×
UNCOV
465
        return rt, nil
×
466
}
467

468
// maxOnionPayloadSize is the largest Sphinx payload possible, so we don't need
469
// to read/write a TLV stream larger than this.
470
const maxOnionPayloadSize = 1300
471

UNCOV
472
func deserializeHop(r io.Reader) (*Hop, error) {
×
UNCOV
473
        h := &Hop{}
×
UNCOV
474

×
UNCOV
475
        var pub []byte
×
UNCOV
476
        if err := ReadElements(r, &pub); err != nil {
×
477
                return nil, err
×
478
        }
×
UNCOV
479
        copy(h.PubKeyBytes[:], pub)
×
UNCOV
480

×
UNCOV
481
        if err := ReadElements(r,
×
UNCOV
482
                &h.ChannelID, &h.OutgoingTimeLock, &h.AmtToForward,
×
UNCOV
483
        ); err != nil {
×
484
                return nil, err
×
485
        }
×
486

487
        // TODO(roasbeef): change field to allow LegacyPayload false to be the
488
        // legacy default?
UNCOV
489
        err := binary.Read(r, byteOrder, &h.LegacyPayload)
×
UNCOV
490
        if err != nil {
×
491
                return nil, err
×
492
        }
×
493

UNCOV
494
        var numElements uint32
×
UNCOV
495
        if err := ReadElements(r, &numElements); err != nil {
×
496
                return nil, err
×
497
        }
×
498

499
        // If there're no elements, then we can return early.
UNCOV
500
        if numElements == 0 {
×
UNCOV
501
                return h, nil
×
UNCOV
502
        }
×
503

UNCOV
504
        tlvMap := make(map[uint64][]byte)
×
UNCOV
505
        for i := uint32(0); i < numElements; i++ {
×
UNCOV
506
                var tlvType uint64
×
UNCOV
507
                if err := ReadElements(r, &tlvType); err != nil {
×
508
                        return nil, err
×
509
                }
×
510

UNCOV
511
                rawRecordBytes, err := wire.ReadVarBytes(
×
UNCOV
512
                        r, 0, maxOnionPayloadSize, "tlv",
×
UNCOV
513
                )
×
UNCOV
514
                if err != nil {
×
515
                        return nil, err
×
516
                }
×
517

UNCOV
518
                tlvMap[tlvType] = rawRecordBytes
×
519
        }
520

521
        // If the MPP type is present, remove it from the generic TLV map and
522
        // parse it back into a proper MPP struct.
523
        //
524
        // TODO(conner): add migration to unify all fields in a single TLV
525
        // blobs. The split approach will cause headaches down the road as more
526
        // fields are added, which we can avoid by having a single TLV stream
527
        // for all payload fields.
UNCOV
528
        mppType := uint64(MPPOnionType)
×
UNCOV
529
        if mppBytes, ok := tlvMap[mppType]; ok {
×
UNCOV
530
                delete(tlvMap, mppType)
×
UNCOV
531

×
UNCOV
532
                var (
×
UNCOV
533
                        mpp    = &MPP{}
×
UNCOV
534
                        mppRec = mpp.Record()
×
UNCOV
535
                        r      = bytes.NewReader(mppBytes)
×
UNCOV
536
                )
×
UNCOV
537
                err := mppRec.Decode(r, uint64(len(mppBytes)))
×
UNCOV
538
                if err != nil {
×
539
                        return nil, err
×
540
                }
×
UNCOV
541
                h.MPP = mpp
×
542
        }
543

544
        // If encrypted data or blinding key are present, remove them from
545
        // the TLV map and parse into proper types.
UNCOV
546
        encryptedDataType := uint64(EncryptedDataOnionType)
×
UNCOV
547
        if data, ok := tlvMap[encryptedDataType]; ok {
×
UNCOV
548
                delete(tlvMap, encryptedDataType)
×
UNCOV
549
                h.EncryptedData = data
×
UNCOV
550
        }
×
551

UNCOV
552
        blindingType := uint64(BlindingPointOnionType)
×
UNCOV
553
        if blindingPoint, ok := tlvMap[blindingType]; ok {
×
UNCOV
554
                delete(tlvMap, blindingType)
×
UNCOV
555

×
UNCOV
556
                h.BlindingPoint, err = btcec.ParsePubKey(blindingPoint)
×
UNCOV
557
                if err != nil {
×
558
                        return nil, fmt.Errorf("invalid blinding point: %w",
×
559
                                err)
×
560
                }
×
561
        }
562

UNCOV
563
        ampType := uint64(AMPOnionType)
×
UNCOV
564
        if ampBytes, ok := tlvMap[ampType]; ok {
×
UNCOV
565
                delete(tlvMap, ampType)
×
UNCOV
566

×
UNCOV
567
                var (
×
UNCOV
568
                        amp    = &AMP{}
×
UNCOV
569
                        ampRec = amp.Record()
×
UNCOV
570
                        r      = bytes.NewReader(ampBytes)
×
UNCOV
571
                )
×
UNCOV
572
                err := ampRec.Decode(r, uint64(len(ampBytes)))
×
UNCOV
573
                if err != nil {
×
574
                        return nil, err
×
575
                }
×
UNCOV
576
                h.AMP = amp
×
577
        }
578

579
        // If the metadata type is present, remove it from the tlv map and
580
        // populate directly on the hop.
UNCOV
581
        metadataType := uint64(MetadataOnionType)
×
UNCOV
582
        if metadata, ok := tlvMap[metadataType]; ok {
×
UNCOV
583
                delete(tlvMap, metadataType)
×
UNCOV
584

×
UNCOV
585
                h.Metadata = metadata
×
UNCOV
586
        }
×
587

UNCOV
588
        totalAmtMsatType := uint64(TotalAmtMsatBlindedType)
×
UNCOV
589
        if totalAmtMsat, ok := tlvMap[totalAmtMsatType]; ok {
×
UNCOV
590
                delete(tlvMap, totalAmtMsatType)
×
UNCOV
591

×
UNCOV
592
                var (
×
UNCOV
593
                        totalAmtMsatInt uint64
×
UNCOV
594
                        buf             [8]byte
×
UNCOV
595
                )
×
UNCOV
596
                if err := tlv.DTUint64(
×
UNCOV
597
                        bytes.NewReader(totalAmtMsat),
×
UNCOV
598
                        &totalAmtMsatInt,
×
UNCOV
599
                        &buf,
×
UNCOV
600
                        uint64(len(totalAmtMsat)),
×
UNCOV
601
                ); err != nil {
×
602
                        return nil, err
×
603
                }
×
604

UNCOV
605
                h.TotalAmtMsat = lnwire.MilliSatoshi(totalAmtMsatInt)
×
606
        }
607

UNCOV
608
        h.CustomRecords = tlvMap
×
UNCOV
609

×
UNCOV
610
        return h, nil
×
611
}
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