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

lightningnetwork / lnd / 13566028875

27 Feb 2025 12:09PM UTC coverage: 49.396% (-9.4%) from 58.748%
13566028875

Pull #9555

github

ellemouton
graph/db: populate the graph cache in Start instead of during construction

In this commit, we move the graph cache population logic out of the
ChannelGraph constructor and into its Start method instead.
Pull Request #9555: graph: extract cache from CRUD [6]

34 of 54 new or added lines in 4 files covered. (62.96%)

27464 existing lines in 436 files now uncovered.

101095 of 204664 relevant lines covered (49.4%)

1.54 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.
×
UNCOV
189
        size := func() uint64 {
×
190
                return 32 + tlv.SizeTUint64(uint64(r.totalMsat))
×
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(
×
UNCOV
300
                AMPOnionType, a, a.PayloadSize, AMPEncoder, AMPDecoder,
×
UNCOV
301
        )
×
UNCOV
302
}
×
303

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

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

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

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

327
        // Any new/extra TLV data is encoded in serializeHTLCAttemptInfo!
328

UNCOV
329
        return nil
×
330
}
331

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
428
        return nil
×
429
}
430

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

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

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

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

×
UNCOV
461
        // Any new/extra TLV data is decoded in deserializeHTLCAttemptInfo!
×
UNCOV
462

×
UNCOV
463
        return rt, nil
×
464
}
465

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

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

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

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

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

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

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

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

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

UNCOV
516
                tlvMap[tlvType] = rawRecordBytes
×
517
        }
518

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

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

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

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

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

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

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

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

×
UNCOV
583
                h.Metadata = metadata
×
UNCOV
584
        }
×
585

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

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

UNCOV
603
                h.TotalAmtMsat = lnwire.MilliSatoshi(totalAmtMsatInt)
×
604
        }
605

UNCOV
606
        h.CustomRecords = tlvMap
×
UNCOV
607

×
UNCOV
608
        return h, nil
×
609
}
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