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

lightningnetwork / lnd / 17132206455

21 Aug 2025 03:56PM UTC coverage: 54.685% (-2.6%) from 57.321%
17132206455

Pull #10167

github

web-flow
Merge 5dd2ed093 into 0c2f045f5
Pull Request #10167: multi: bump Go to 1.24.6

4 of 31 new or added lines in 10 files covered. (12.9%)

23854 existing lines in 284 files now uncovered.

108937 of 199210 relevant lines covered (54.68%)

22026.48 hits per line

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

46.37
/lnrpc/routerrpc/router_backend.go
1
package routerrpc
2

3
import (
4
        "context"
5
        "crypto/rand"
6
        "encoding/hex"
7
        "errors"
8
        "fmt"
9
        math "math"
10
        "time"
11

12
        "github.com/btcsuite/btcd/btcec/v2"
13
        "github.com/btcsuite/btcd/btcutil"
14
        "github.com/btcsuite/btcd/chaincfg"
15
        "github.com/btcsuite/btcd/wire"
16
        sphinx "github.com/lightningnetwork/lightning-onion"
17
        "github.com/lightningnetwork/lnd/clock"
18
        "github.com/lightningnetwork/lnd/feature"
19
        "github.com/lightningnetwork/lnd/fn/v2"
20
        "github.com/lightningnetwork/lnd/htlcswitch"
21
        "github.com/lightningnetwork/lnd/lnrpc"
22
        "github.com/lightningnetwork/lnd/lntypes"
23
        "github.com/lightningnetwork/lnd/lnwire"
24
        paymentsdb "github.com/lightningnetwork/lnd/payments/db"
25
        "github.com/lightningnetwork/lnd/record"
26
        "github.com/lightningnetwork/lnd/routing"
27
        "github.com/lightningnetwork/lnd/routing/route"
28
        "github.com/lightningnetwork/lnd/subscribe"
29
        "github.com/lightningnetwork/lnd/zpay32"
30
        "google.golang.org/protobuf/proto"
31
)
32

33
const (
34
        // DefaultMaxParts is the default number of splits we'll possibly use
35
        // for MPP when the user is attempting to send a payment.
36
        //
37
        // TODO(roasbeef): make this value dynamic based on expected number of
38
        // attempts for given amount.
39
        DefaultMaxParts = 16
40

41
        // MaxPartsUpperLimit defines the maximum allowable number of splits
42
        // for MPP/AMP when the user is attempting to send a payment.
43
        MaxPartsUpperLimit = 1000
44
)
45

46
// RouterBackend contains the backend implementation of the router rpc sub
47
// server calls.
48
type RouterBackend struct {
49
        // SelfNode is the vertex of the node sending the payment.
50
        SelfNode route.Vertex
51

52
        // FetchChannelCapacity is a closure that we'll use the fetch the total
53
        // capacity of a channel to populate in responses.
54
        FetchChannelCapacity func(chanID uint64) (btcutil.Amount, error)
55

56
        // FetchAmountPairCapacity determines the maximal channel capacity
57
        // between two nodes given a certain amount.
58
        FetchAmountPairCapacity func(nodeFrom, nodeTo route.Vertex,
59
                amount lnwire.MilliSatoshi) (btcutil.Amount, error)
60

61
        // FetchChannelEndpoints returns the pubkeys of both endpoints of the
62
        // given channel id.
63
        FetchChannelEndpoints func(chanID uint64) (route.Vertex,
64
                route.Vertex, error)
65

66
        // FindRoute is a closure that abstracts away how we locate/query for
67
        // routes.
68
        FindRoute func(*routing.RouteRequest) (*route.Route, float64, error)
69

70
        MissionControl MissionControl
71

72
        // ActiveNetParams are the network parameters of the primary network
73
        // that the route is operating on. This is necessary so we can ensure
74
        // that we receive payment requests that send to destinations on our
75
        // network.
76
        ActiveNetParams *chaincfg.Params
77

78
        // Tower is the ControlTower instance that is used to track pending
79
        // payments.
80
        Tower routing.ControlTower
81

82
        // MaxTotalTimelock is the maximum total time lock a route is allowed to
83
        // have.
84
        MaxTotalTimelock uint32
85

86
        // DefaultFinalCltvDelta is the default value used as final cltv delta
87
        // when an RPC caller doesn't specify a value.
88
        DefaultFinalCltvDelta uint16
89

90
        // SubscribeHtlcEvents returns a subscription client for the node's
91
        // htlc events.
92
        SubscribeHtlcEvents func() (*subscribe.Client, error)
93

94
        // InterceptableForwarder exposes the ability to intercept forward events
95
        // by letting the router register a ForwardInterceptor.
96
        InterceptableForwarder htlcswitch.InterceptableHtlcForwarder
97

98
        // SetChannelEnabled exposes the ability to manually enable a channel.
99
        SetChannelEnabled func(wire.OutPoint) error
100

101
        // SetChannelDisabled exposes the ability to manually disable a channel
102
        SetChannelDisabled func(wire.OutPoint) error
103

104
        // SetChannelAuto exposes the ability to restore automatic channel state
105
        // management after manually setting channel status.
106
        SetChannelAuto func(wire.OutPoint) error
107

108
        // UseStatusInitiated is a boolean that indicates whether the router
109
        // should use the new status code `Payment_INITIATED`.
110
        //
111
        // TODO(yy): remove this config after the new status code is fully
112
        // deployed to the network(v0.20.0).
113
        UseStatusInitiated bool
114

115
        // ParseCustomChannelData is a function that can be used to parse custom
116
        // channel data from the first hop of a route.
117
        ParseCustomChannelData func(message proto.Message) error
118

119
        // ShouldSetExpEndorsement returns a boolean indicating whether the
120
        // experimental endorsement bit should be set.
121
        ShouldSetExpEndorsement func() bool
122

123
        // Clock is the clock used to validate payment requests expiry.
124
        // It is useful for testing.
125
        Clock clock.Clock
126
}
127

128
// MissionControl defines the mission control dependencies of routerrpc.
129
type MissionControl interface {
130
        // GetProbability is expected to return the success probability of a
131
        // payment from fromNode to toNode.
132
        GetProbability(fromNode, toNode route.Vertex,
133
                amt lnwire.MilliSatoshi, capacity btcutil.Amount) float64
134

135
        // ResetHistory resets the history of MissionControl returning it to a
136
        // state as if no payment attempts have been made.
137
        ResetHistory() error
138

139
        // GetHistorySnapshot takes a snapshot from the current mission control
140
        // state and actual probability estimates.
141
        GetHistorySnapshot() *routing.MissionControlSnapshot
142

143
        // ImportHistory imports the mission control snapshot to our internal
144
        // state. This import will only be applied in-memory, and will not be
145
        // persisted across restarts.
146
        ImportHistory(snapshot *routing.MissionControlSnapshot, force bool) error
147

148
        // GetPairHistorySnapshot returns the stored history for a given node
149
        // pair.
150
        GetPairHistorySnapshot(fromNode,
151
                toNode route.Vertex) routing.TimedPairResult
152

153
        // GetConfig gets mission control's current config.
154
        GetConfig() *routing.MissionControlConfig
155

156
        // SetConfig sets mission control's config to the values provided, if
157
        // they are valid.
158
        SetConfig(cfg *routing.MissionControlConfig) error
159
}
160

161
// QueryRoutes attempts to query the daemons' Channel Router for a possible
162
// route to a target destination capable of carrying a specific amount of
163
// satoshis within the route's flow. The returned route contains the full
164
// details required to craft and send an HTLC, also including the necessary
165
// information that should be present within the Sphinx packet encapsulated
166
// within the HTLC.
167
//
168
// TODO(roasbeef): should return a slice of routes in reality * create separate
169
// PR to send based on well formatted route
170
func (r *RouterBackend) QueryRoutes(ctx context.Context,
171
        in *lnrpc.QueryRoutesRequest) (*lnrpc.QueryRoutesResponse, error) {
6✔
172

6✔
173
        routeReq, err := r.parseQueryRoutesRequest(in)
6✔
174
        if err != nil {
8✔
175
                return nil, err
2✔
176
        }
2✔
177

178
        // Query the channel router for a possible path to the destination that
179
        // can carry `in.Amt` satoshis _including_ the total fee required on
180
        // the route
181
        route, successProb, err := r.FindRoute(routeReq)
4✔
182
        if err != nil {
4✔
183
                return nil, err
×
184
        }
×
185

186
        // For each valid route, we'll convert the result into the format
187
        // required by the RPC system.
188
        rpcRoute, err := r.MarshallRoute(route)
4✔
189
        if err != nil {
4✔
190
                return nil, err
×
191
        }
×
192

193
        routeResp := &lnrpc.QueryRoutesResponse{
4✔
194
                Routes:      []*lnrpc.Route{rpcRoute},
4✔
195
                SuccessProb: successProb,
4✔
196
        }
4✔
197

4✔
198
        return routeResp, nil
4✔
199
}
200

201
func parsePubKey(key string) (route.Vertex, error) {
6✔
202
        pubKeyBytes, err := hex.DecodeString(key)
6✔
203
        if err != nil {
6✔
204
                return route.Vertex{}, err
×
205
        }
×
206

207
        return route.NewVertexFromBytes(pubKeyBytes)
6✔
208
}
209

210
func (r *RouterBackend) parseIgnored(in *lnrpc.QueryRoutesRequest) (
211
        map[route.Vertex]struct{}, map[routing.DirectedNodePair]struct{},
212
        error) {
5✔
213

5✔
214
        ignoredNodes := make(map[route.Vertex]struct{})
5✔
215
        for _, ignorePubKey := range in.IgnoredNodes {
10✔
216
                ignoreVertex, err := route.NewVertexFromBytes(ignorePubKey)
5✔
217
                if err != nil {
5✔
218
                        return nil, nil, err
×
219
                }
×
220
                ignoredNodes[ignoreVertex] = struct{}{}
5✔
221
        }
222

223
        ignoredPairs := make(map[routing.DirectedNodePair]struct{})
5✔
224

5✔
225
        // Convert deprecated ignoredEdges to pairs.
5✔
226
        for _, ignoredEdge := range in.IgnoredEdges {
10✔
227
                pair, err := r.rpcEdgeToPair(ignoredEdge)
5✔
228
                if err != nil {
5✔
229
                        log.Warnf("Ignore channel %v skipped: %v",
×
230
                                ignoredEdge.ChannelId, err)
×
231

×
232
                        continue
×
233
                }
234
                ignoredPairs[pair] = struct{}{}
5✔
235
        }
236

237
        // Add ignored pairs to set.
238
        for _, ignorePair := range in.IgnoredPairs {
10✔
239
                from, err := route.NewVertexFromBytes(ignorePair.From)
5✔
240
                if err != nil {
5✔
241
                        return nil, nil, err
×
242
                }
×
243

244
                to, err := route.NewVertexFromBytes(ignorePair.To)
5✔
245
                if err != nil {
5✔
246
                        return nil, nil, err
×
247
                }
×
248

249
                pair := routing.NewDirectedNodePair(from, to)
5✔
250
                ignoredPairs[pair] = struct{}{}
5✔
251
        }
252

253
        return ignoredNodes, ignoredPairs, nil
5✔
254
}
255

256
func (r *RouterBackend) parseQueryRoutesRequest(in *lnrpc.QueryRoutesRequest) (
257
        *routing.RouteRequest, error) {
6✔
258

6✔
259
        // Parse the hex-encoded source public key into a full public key that
6✔
260
        // we can properly manipulate.
6✔
261

6✔
262
        var sourcePubKey route.Vertex
6✔
263
        if in.SourcePubKey != "" {
6✔
264
                var err error
×
265
                sourcePubKey, err = parsePubKey(in.SourcePubKey)
×
266
                if err != nil {
×
267
                        return nil, err
×
268
                }
×
269
        } else {
6✔
270
                // If no source is specified, use self.
6✔
271
                sourcePubKey = r.SelfNode
6✔
272
        }
6✔
273

274
        // Currently, within the bootstrap phase of the network, we limit the
275
        // largest payment size allotted to (2^32) - 1 mSAT or 4.29 million
276
        // satoshis.
277
        amt, err := lnrpc.UnmarshallAmt(in.Amt, in.AmtMsat)
6✔
278
        if err != nil {
6✔
279
                return nil, err
×
280
        }
×
281

282
        // Unmarshall restrictions from request.
283
        feeLimit := lnrpc.CalculateFeeLimit(in.FeeLimit, amt)
6✔
284

6✔
285
        // Since QueryRoutes allows having a different source other than
6✔
286
        // ourselves, we'll only apply our max time lock if we are the source.
6✔
287
        maxTotalTimelock := r.MaxTotalTimelock
6✔
288
        if sourcePubKey != r.SelfNode {
6✔
289
                maxTotalTimelock = math.MaxUint32
×
290
        }
×
291

292
        cltvLimit, err := ValidateCLTVLimit(in.CltvLimit, maxTotalTimelock)
6✔
293
        if err != nil {
6✔
294
                return nil, err
×
295
        }
×
296

297
        // If we have a blinded path set, we'll get a few of our fields from
298
        // inside of the path rather than the request's fields.
299
        var (
6✔
300
                targetPubKey   *route.Vertex
6✔
301
                routeHintEdges map[route.Vertex][]routing.AdditionalEdge
6✔
302
                blindedPathSet *routing.BlindedPaymentPathSet
6✔
303

6✔
304
                // finalCLTVDelta varies depending on whether we're sending to
6✔
305
                // a blinded route or an unblinded node. For blinded paths,
6✔
306
                // our final cltv is already baked into the path so we restrict
6✔
307
                // this value to zero on the API. Bolt11 invoices have a
6✔
308
                // default, so we'll fill that in for the non-blinded case.
6✔
309
                finalCLTVDelta uint16
6✔
310

6✔
311
                // destinationFeatures is the set of features for the
6✔
312
                // destination node.
6✔
313
                destinationFeatures *lnwire.FeatureVector
6✔
314
        )
6✔
315

6✔
316
        // Validate that the fields provided in the request are sane depending
6✔
317
        // on whether it is using a blinded path or not.
6✔
318
        if len(in.BlindedPaymentPaths) > 0 {
6✔
UNCOV
319
                blindedPathSet, err = parseBlindedPaymentPaths(in)
×
UNCOV
320
                if err != nil {
×
321
                        return nil, err
×
322
                }
×
323

UNCOV
324
                pathFeatures := blindedPathSet.Features()
×
UNCOV
325
                if pathFeatures != nil {
×
326
                        destinationFeatures = pathFeatures.Clone()
×
327
                }
×
328
        } else {
6✔
329
                // If we do not have a blinded path, a target pubkey must be
6✔
330
                // set.
6✔
331
                pk, err := parsePubKey(in.PubKey)
6✔
332
                if err != nil {
6✔
333
                        return nil, err
×
334
                }
×
335
                targetPubKey = &pk
6✔
336

6✔
337
                // Convert route hints to an edge map.
6✔
338
                routeHints, err := unmarshallRouteHints(in.RouteHints)
6✔
339
                if err != nil {
6✔
340
                        return nil, err
×
341
                }
×
342

343
                routeHintEdges, err = routing.RouteHintsToEdges(
6✔
344
                        routeHints, *targetPubKey,
6✔
345
                )
6✔
346
                if err != nil {
6✔
347
                        return nil, err
×
348
                }
×
349

350
                // Set a non-zero final CLTV delta for payments that are not
351
                // to blinded paths, as bolt11 has a default final cltv delta
352
                // value that is used in the absence of a value.
353
                finalCLTVDelta = r.DefaultFinalCltvDelta
6✔
354
                if in.FinalCltvDelta != 0 {
12✔
355
                        finalCLTVDelta = uint16(in.FinalCltvDelta)
6✔
356
                }
6✔
357

358
                // Do bounds checking without block padding so we don't give
359
                // routes that will leave the router in a zombie payment state.
360
                err = routing.ValidateCLTVLimit(
6✔
361
                        cltvLimit, finalCLTVDelta, false,
6✔
362
                )
6✔
363
                if err != nil {
7✔
364
                        return nil, err
1✔
365
                }
1✔
366

367
                // Parse destination feature bits.
368
                destinationFeatures, err = UnmarshalFeatures(in.DestFeatures)
5✔
369
                if err != nil {
5✔
370
                        return nil, err
×
371
                }
×
372
        }
373

374
        // We need to subtract the final delta before passing it into path
375
        // finding. The optimal path is independent of the final cltv delta and
376
        // the path finding algorithm is unaware of this value.
377
        cltvLimit -= uint32(finalCLTVDelta)
5✔
378

5✔
379
        ignoredNodes, ignoredPairs, err := r.parseIgnored(in)
5✔
380
        if err != nil {
5✔
381
                return nil, err
×
382
        }
×
383

384
        restrictions := &routing.RestrictParams{
5✔
385
                FeeLimit: feeLimit,
5✔
386
                ProbabilitySource: func(fromNode, toNode route.Vertex,
5✔
387
                        amt lnwire.MilliSatoshi,
5✔
388
                        capacity btcutil.Amount) float64 {
21✔
389

16✔
390
                        if _, ok := ignoredNodes[fromNode]; ok {
20✔
391
                                return 0
4✔
392
                        }
4✔
393

394
                        pair := routing.DirectedNodePair{
12✔
395
                                From: fromNode,
12✔
396
                                To:   toNode,
12✔
397
                        }
12✔
398
                        if _, ok := ignoredPairs[pair]; ok {
20✔
399
                                return 0
8✔
400
                        }
8✔
401

402
                        if !in.UseMissionControl {
7✔
403
                                return 1
3✔
404
                        }
3✔
405

406
                        return r.MissionControl.GetProbability(
1✔
407
                                fromNode, toNode, amt, capacity,
1✔
408
                        )
1✔
409
                },
410
                DestCustomRecords:     record.CustomSet(in.DestCustomRecords),
411
                CltvLimit:             cltvLimit,
412
                DestFeatures:          destinationFeatures,
413
                BlindedPaymentPathSet: blindedPathSet,
414
        }
415

416
        // We set the outgoing channel restrictions if the user provides a
417
        // list of channel ids. We also handle the case where the user
418
        // provides the deprecated `OutgoingChanId` field.
419
        switch {
5✔
420
        case len(in.OutgoingChanIds) > 0 && in.OutgoingChanId != 0:
1✔
421
                return nil, errors.New("outgoing_chan_id and " +
1✔
422
                        "outgoing_chan_ids cannot both be set")
1✔
423

424
        case len(in.OutgoingChanIds) > 0:
1✔
425
                restrictions.OutgoingChannelIDs = in.OutgoingChanIds
1✔
426

427
        case in.OutgoingChanId != 0:
3✔
428
                restrictions.OutgoingChannelIDs = []uint64{in.OutgoingChanId}
3✔
429
        }
430

431
        // Pass along a last hop restriction if specified.
432
        if len(in.LastHopPubkey) > 0 {
8✔
433
                lastHop, err := route.NewVertexFromBytes(
4✔
434
                        in.LastHopPubkey,
4✔
435
                )
4✔
436
                if err != nil {
4✔
437
                        return nil, err
×
438
                }
×
439
                restrictions.LastHop = &lastHop
4✔
440
        }
441

442
        // If we have any TLV records destined for the final hop, then we'll
443
        // attempt to decode them now into a form that the router can more
444
        // easily manipulate.
445
        customRecords := record.CustomSet(in.DestCustomRecords)
4✔
446
        if err := customRecords.Validate(); err != nil {
4✔
447
                return nil, err
×
448
        }
×
449

450
        return routing.NewRouteRequest(
4✔
451
                sourcePubKey, targetPubKey, amt, in.TimePref, restrictions,
4✔
452
                customRecords, routeHintEdges, blindedPathSet,
4✔
453
                finalCLTVDelta,
4✔
454
        )
4✔
455
}
456

457
func parseBlindedPaymentPaths(in *lnrpc.QueryRoutesRequest) (
UNCOV
458
        *routing.BlindedPaymentPathSet, error) {
×
UNCOV
459

×
UNCOV
460
        if len(in.PubKey) != 0 {
×
461
                return nil, fmt.Errorf("target pubkey: %x should not be set "+
×
462
                        "when blinded path is provided", in.PubKey)
×
463
        }
×
464

UNCOV
465
        if len(in.RouteHints) > 0 {
×
466
                return nil, errors.New("route hints and blinded path can't " +
×
467
                        "both be set")
×
468
        }
×
469

UNCOV
470
        if in.FinalCltvDelta != 0 {
×
471
                return nil, errors.New("final cltv delta should be " +
×
472
                        "zero for blinded paths")
×
473
        }
×
474

475
        // For blinded paths, we get one set of features for the relaying
476
        // intermediate nodes and the final destination. We don't allow the
477
        // destination feature bit field for regular payments to be set, as
478
        // this could lead to ambiguity.
UNCOV
479
        if len(in.DestFeatures) > 0 {
×
480
                return nil, errors.New("destination features should " +
×
481
                        "be populated in blinded path")
×
482
        }
×
483

UNCOV
484
        paths := make([]*routing.BlindedPayment, len(in.BlindedPaymentPaths))
×
UNCOV
485
        for i, paymentPath := range in.BlindedPaymentPaths {
×
UNCOV
486
                blindedPmt, err := unmarshalBlindedPayment(paymentPath)
×
UNCOV
487
                if err != nil {
×
488
                        return nil, fmt.Errorf("parse blinded payment: %w", err)
×
489
                }
×
490

UNCOV
491
                if err := blindedPmt.Validate(); err != nil {
×
492
                        return nil, fmt.Errorf("invalid blinded path: %w", err)
×
493
                }
×
494

UNCOV
495
                paths[i] = blindedPmt
×
496
        }
497

UNCOV
498
        return routing.NewBlindedPaymentPathSet(paths)
×
499
}
500

501
func unmarshalBlindedPayment(rpcPayment *lnrpc.BlindedPaymentPath) (
UNCOV
502
        *routing.BlindedPayment, error) {
×
UNCOV
503

×
UNCOV
504
        if rpcPayment == nil {
×
505
                return nil, errors.New("nil blinded payment")
×
506
        }
×
507

UNCOV
508
        path, err := unmarshalBlindedPaymentPaths(rpcPayment.BlindedPath)
×
UNCOV
509
        if err != nil {
×
510
                return nil, err
×
511
        }
×
512

UNCOV
513
        features, err := UnmarshalFeatures(rpcPayment.Features)
×
UNCOV
514
        if err != nil {
×
515
                return nil, err
×
516
        }
×
517

UNCOV
518
        return &routing.BlindedPayment{
×
UNCOV
519
                BlindedPath:         path,
×
UNCOV
520
                CltvExpiryDelta:     uint16(rpcPayment.TotalCltvDelta),
×
UNCOV
521
                BaseFee:             uint32(rpcPayment.BaseFeeMsat),
×
UNCOV
522
                ProportionalFeeRate: rpcPayment.ProportionalFeeRate,
×
UNCOV
523
                HtlcMinimum:         rpcPayment.HtlcMinMsat,
×
UNCOV
524
                HtlcMaximum:         rpcPayment.HtlcMaxMsat,
×
UNCOV
525
                Features:            features,
×
UNCOV
526
        }, nil
×
527
}
528

529
func unmarshalBlindedPaymentPaths(rpcPath *lnrpc.BlindedPath) (
UNCOV
530
        *sphinx.BlindedPath, error) {
×
UNCOV
531

×
UNCOV
532
        if rpcPath == nil {
×
533
                return nil, errors.New("blinded path required when blinded " +
×
534
                        "route is provided")
×
535
        }
×
536

UNCOV
537
        introduction, err := btcec.ParsePubKey(rpcPath.IntroductionNode)
×
UNCOV
538
        if err != nil {
×
539
                return nil, err
×
540
        }
×
541

UNCOV
542
        blinding, err := btcec.ParsePubKey(rpcPath.BlindingPoint)
×
UNCOV
543
        if err != nil {
×
544
                return nil, err
×
545
        }
×
546

UNCOV
547
        if len(rpcPath.BlindedHops) < 1 {
×
548
                return nil, errors.New("at least 1 blinded hops required")
×
549
        }
×
550

UNCOV
551
        path := &sphinx.BlindedPath{
×
UNCOV
552
                IntroductionPoint: introduction,
×
UNCOV
553
                BlindingPoint:     blinding,
×
UNCOV
554
                BlindedHops: make(
×
UNCOV
555
                        []*sphinx.BlindedHopInfo, len(rpcPath.BlindedHops),
×
UNCOV
556
                ),
×
UNCOV
557
        }
×
UNCOV
558

×
UNCOV
559
        for i, hop := range rpcPath.BlindedHops {
×
UNCOV
560
                path.BlindedHops[i], err = unmarshalBlindedHop(hop)
×
UNCOV
561
                if err != nil {
×
562
                        return nil, err
×
563
                }
×
564
        }
565

UNCOV
566
        return path, nil
×
567
}
568

569
func unmarshalBlindedHop(rpcHop *lnrpc.BlindedHop) (*sphinx.BlindedHopInfo,
UNCOV
570
        error) {
×
UNCOV
571

×
UNCOV
572
        pubkey, err := btcec.ParsePubKey(rpcHop.BlindedNode)
×
UNCOV
573
        if err != nil {
×
574
                return nil, err
×
575
        }
×
576

UNCOV
577
        if len(rpcHop.EncryptedData) == 0 {
×
578
                return nil, errors.New("empty encrypted data not allowed")
×
579
        }
×
580

UNCOV
581
        return &sphinx.BlindedHopInfo{
×
UNCOV
582
                BlindedNodePub: pubkey,
×
UNCOV
583
                CipherText:     rpcHop.EncryptedData,
×
UNCOV
584
        }, nil
×
585
}
586

587
// rpcEdgeToPair looks up the provided channel and returns the channel endpoints
588
// as a directed pair.
589
func (r *RouterBackend) rpcEdgeToPair(e *lnrpc.EdgeLocator) (
590
        routing.DirectedNodePair, error) {
5✔
591

5✔
592
        a, b, err := r.FetchChannelEndpoints(e.ChannelId)
5✔
593
        if err != nil {
5✔
594
                return routing.DirectedNodePair{}, err
×
595
        }
×
596

597
        var pair routing.DirectedNodePair
5✔
598
        if e.DirectionReverse {
10✔
599
                pair.From, pair.To = b, a
5✔
600
        } else {
5✔
601
                pair.From, pair.To = a, b
×
602
        }
×
603

604
        return pair, nil
5✔
605
}
606

607
// MarshallRoute marshalls an internal route to an rpc route struct.
608
func (r *RouterBackend) MarshallRoute(route *route.Route) (*lnrpc.Route, error) {
4✔
609
        resp := &lnrpc.Route{
4✔
610
                TotalTimeLock:      route.TotalTimeLock,
4✔
611
                TotalFees:          int64(route.TotalFees().ToSatoshis()),
4✔
612
                TotalFeesMsat:      int64(route.TotalFees()),
4✔
613
                TotalAmt:           int64(route.TotalAmount.ToSatoshis()),
4✔
614
                TotalAmtMsat:       int64(route.TotalAmount),
4✔
615
                Hops:               make([]*lnrpc.Hop, len(route.Hops)),
4✔
616
                FirstHopAmountMsat: int64(route.FirstHopAmount.Val.Int()),
4✔
617
        }
4✔
618

4✔
619
        // Encode the route's custom channel data (if available).
4✔
620
        if len(route.FirstHopWireCustomRecords) > 0 {
4✔
UNCOV
621
                customData, err := route.FirstHopWireCustomRecords.Serialize()
×
UNCOV
622
                if err != nil {
×
623
                        return nil, err
×
624
                }
×
625

UNCOV
626
                resp.CustomChannelData = customData
×
UNCOV
627

×
UNCOV
628
                // Allow the aux data parser to parse the custom records into
×
UNCOV
629
                // a human-readable JSON (if available).
×
UNCOV
630
                if r.ParseCustomChannelData != nil {
×
UNCOV
631
                        err := r.ParseCustomChannelData(resp)
×
UNCOV
632
                        if err != nil {
×
633
                                return nil, err
×
634
                        }
×
635
                }
636
        }
637

638
        incomingAmt := route.TotalAmount
4✔
639
        for i, hop := range route.Hops {
8✔
640
                fee := route.HopFee(i)
4✔
641

4✔
642
                // Channel capacity is not a defining property of a route. For
4✔
643
                // backwards RPC compatibility, we retrieve it here from the
4✔
644
                // graph.
4✔
645
                chanCapacity, err := r.FetchChannelCapacity(hop.ChannelID)
4✔
646
                if err != nil {
4✔
UNCOV
647
                        // If capacity cannot be retrieved, this may be a
×
UNCOV
648
                        // not-yet-received or private channel. Then report
×
UNCOV
649
                        // amount that is sent through the channel as capacity.
×
UNCOV
650
                        chanCapacity = incomingAmt.ToSatoshis()
×
UNCOV
651
                }
×
652

653
                // Extract the MPP fields if present on this hop.
654
                var mpp *lnrpc.MPPRecord
4✔
655
                if hop.MPP != nil {
4✔
UNCOV
656
                        addr := hop.MPP.PaymentAddr()
×
UNCOV
657

×
UNCOV
658
                        mpp = &lnrpc.MPPRecord{
×
UNCOV
659
                                PaymentAddr:  addr[:],
×
UNCOV
660
                                TotalAmtMsat: int64(hop.MPP.TotalMsat()),
×
UNCOV
661
                        }
×
UNCOV
662
                }
×
663

664
                var amp *lnrpc.AMPRecord
4✔
665
                if hop.AMP != nil {
4✔
UNCOV
666
                        rootShare := hop.AMP.RootShare()
×
UNCOV
667
                        setID := hop.AMP.SetID()
×
UNCOV
668

×
UNCOV
669
                        amp = &lnrpc.AMPRecord{
×
UNCOV
670
                                RootShare:  rootShare[:],
×
UNCOV
671
                                SetId:      setID[:],
×
UNCOV
672
                                ChildIndex: hop.AMP.ChildIndex(),
×
UNCOV
673
                        }
×
UNCOV
674
                }
×
675

676
                resp.Hops[i] = &lnrpc.Hop{
4✔
677
                        ChanId:           hop.ChannelID,
4✔
678
                        ChanCapacity:     int64(chanCapacity),
4✔
679
                        AmtToForward:     int64(hop.AmtToForward.ToSatoshis()),
4✔
680
                        AmtToForwardMsat: int64(hop.AmtToForward),
4✔
681
                        Fee:              int64(fee.ToSatoshis()),
4✔
682
                        FeeMsat:          int64(fee),
4✔
683
                        Expiry:           uint32(hop.OutgoingTimeLock),
4✔
684
                        PubKey: hex.EncodeToString(
4✔
685
                                hop.PubKeyBytes[:],
4✔
686
                        ),
4✔
687
                        CustomRecords: hop.CustomRecords,
4✔
688
                        TlvPayload:    !hop.LegacyPayload,
4✔
689
                        MppRecord:     mpp,
4✔
690
                        AmpRecord:     amp,
4✔
691
                        Metadata:      hop.Metadata,
4✔
692
                        EncryptedData: hop.EncryptedData,
4✔
693
                        TotalAmtMsat:  uint64(hop.TotalAmtMsat),
4✔
694
                }
4✔
695

4✔
696
                if hop.BlindingPoint != nil {
4✔
UNCOV
697
                        blinding := hop.BlindingPoint.SerializeCompressed()
×
UNCOV
698
                        resp.Hops[i].BlindingPoint = blinding
×
UNCOV
699
                }
×
700
                incomingAmt = hop.AmtToForward
4✔
701
        }
702

703
        return resp, nil
4✔
704
}
705

706
// UnmarshallHopWithPubkey unmarshalls an rpc hop for which the pubkey has
707
// already been extracted.
708
func UnmarshallHopWithPubkey(rpcHop *lnrpc.Hop, pubkey route.Vertex) (*route.Hop,
UNCOV
709
        error) {
×
UNCOV
710

×
UNCOV
711
        customRecords := record.CustomSet(rpcHop.CustomRecords)
×
UNCOV
712
        if err := customRecords.Validate(); err != nil {
×
713
                return nil, err
×
714
        }
×
715

UNCOV
716
        mpp, err := UnmarshalMPP(rpcHop.MppRecord)
×
UNCOV
717
        if err != nil {
×
718
                return nil, err
×
719
        }
×
720

UNCOV
721
        amp, err := UnmarshalAMP(rpcHop.AmpRecord)
×
UNCOV
722
        if err != nil {
×
723
                return nil, err
×
724
        }
×
725

UNCOV
726
        hop := &route.Hop{
×
UNCOV
727
                OutgoingTimeLock: rpcHop.Expiry,
×
UNCOV
728
                AmtToForward:     lnwire.MilliSatoshi(rpcHop.AmtToForwardMsat),
×
UNCOV
729
                PubKeyBytes:      pubkey,
×
UNCOV
730
                ChannelID:        rpcHop.ChanId,
×
UNCOV
731
                CustomRecords:    customRecords,
×
UNCOV
732
                LegacyPayload:    false,
×
UNCOV
733
                MPP:              mpp,
×
UNCOV
734
                AMP:              amp,
×
UNCOV
735
                EncryptedData:    rpcHop.EncryptedData,
×
UNCOV
736
                TotalAmtMsat:     lnwire.MilliSatoshi(rpcHop.TotalAmtMsat),
×
UNCOV
737
        }
×
UNCOV
738

×
UNCOV
739
        haveBlindingPoint := len(rpcHop.BlindingPoint) != 0
×
UNCOV
740
        if haveBlindingPoint {
×
UNCOV
741
                hop.BlindingPoint, err = btcec.ParsePubKey(
×
UNCOV
742
                        rpcHop.BlindingPoint,
×
UNCOV
743
                )
×
UNCOV
744
                if err != nil {
×
745
                        return nil, fmt.Errorf("blinding point: %w", err)
×
746
                }
×
747
        }
748

UNCOV
749
        if haveBlindingPoint && len(rpcHop.EncryptedData) == 0 {
×
750
                return nil, errors.New("encrypted data should be present if " +
×
751
                        "blinding point is provided")
×
752
        }
×
753

UNCOV
754
        return hop, nil
×
755
}
756

757
// UnmarshallHop unmarshalls an rpc hop that may or may not contain a node
758
// pubkey.
759
func (r *RouterBackend) UnmarshallHop(rpcHop *lnrpc.Hop,
UNCOV
760
        prevNodePubKey [33]byte) (*route.Hop, error) {
×
UNCOV
761

×
UNCOV
762
        var pubKeyBytes [33]byte
×
UNCOV
763
        if rpcHop.PubKey != "" {
×
UNCOV
764
                // Unmarshall the provided hop pubkey.
×
UNCOV
765
                pubKey, err := hex.DecodeString(rpcHop.PubKey)
×
UNCOV
766
                if err != nil {
×
767
                        return nil, fmt.Errorf("cannot decode pubkey %s",
×
768
                                rpcHop.PubKey)
×
769
                }
×
UNCOV
770
                copy(pubKeyBytes[:], pubKey)
×
771
        } else {
×
772
                // If no pub key is given of the hop, the local channel graph
×
773
                // needs to be queried to complete the information necessary for
×
774
                // routing. Discard edge policies, because they may be nil.
×
775
                node1, node2, err := r.FetchChannelEndpoints(rpcHop.ChanId)
×
776
                if err != nil {
×
777
                        return nil, err
×
778
                }
×
779

780
                switch {
×
781
                case prevNodePubKey == node1:
×
782
                        pubKeyBytes = node2
×
783
                case prevNodePubKey == node2:
×
784
                        pubKeyBytes = node1
×
785
                default:
×
786
                        return nil, fmt.Errorf("channel edge does not match " +
×
787
                                "expected node")
×
788
                }
789
        }
790

UNCOV
791
        return UnmarshallHopWithPubkey(rpcHop, pubKeyBytes)
×
792
}
793

794
// UnmarshallRoute unmarshalls an rpc route. For hops that don't specify a
795
// pubkey, the channel graph is queried.
796
func (r *RouterBackend) UnmarshallRoute(rpcroute *lnrpc.Route) (
UNCOV
797
        *route.Route, error) {
×
UNCOV
798

×
UNCOV
799
        prevNodePubKey := r.SelfNode
×
UNCOV
800

×
UNCOV
801
        hops := make([]*route.Hop, len(rpcroute.Hops))
×
UNCOV
802
        for i, hop := range rpcroute.Hops {
×
UNCOV
803
                routeHop, err := r.UnmarshallHop(hop, prevNodePubKey)
×
UNCOV
804
                if err != nil {
×
805
                        return nil, err
×
806
                }
×
807

UNCOV
808
                hops[i] = routeHop
×
UNCOV
809

×
UNCOV
810
                prevNodePubKey = routeHop.PubKeyBytes
×
811
        }
812

UNCOV
813
        route, err := route.NewRouteFromHops(
×
UNCOV
814
                lnwire.MilliSatoshi(rpcroute.TotalAmtMsat),
×
UNCOV
815
                rpcroute.TotalTimeLock,
×
UNCOV
816
                r.SelfNode,
×
UNCOV
817
                hops,
×
UNCOV
818
        )
×
UNCOV
819
        if err != nil {
×
820
                return nil, err
×
821
        }
×
822

UNCOV
823
        return route, nil
×
824
}
825

826
// extractIntentFromSendRequest attempts to parse the SendRequest details
827
// required to dispatch a client from the information presented by an RPC
828
// client.
829
func (r *RouterBackend) extractIntentFromSendRequest(
830
        rpcPayReq *SendPaymentRequest) (*routing.LightningPayment, error) {
24✔
831

24✔
832
        payIntent := &routing.LightningPayment{}
24✔
833

24✔
834
        // Pass along time preference.
24✔
835
        if rpcPayReq.TimePref < -1 || rpcPayReq.TimePref > 1 {
25✔
836
                return nil, errors.New("time preference out of range")
1✔
837
        }
1✔
838
        payIntent.TimePref = rpcPayReq.TimePref
23✔
839

23✔
840
        // Pass along restrictions on the outgoing channels that may be used.
23✔
841
        payIntent.OutgoingChannelIDs = rpcPayReq.OutgoingChanIds
23✔
842

23✔
843
        // Add the deprecated single outgoing channel restriction if present.
23✔
844
        if rpcPayReq.OutgoingChanId != 0 {
24✔
845
                if payIntent.OutgoingChannelIDs != nil {
2✔
846
                        return nil, errors.New("outgoing_chan_id and " +
1✔
847
                                "outgoing_chan_ids are mutually exclusive")
1✔
848
                }
1✔
849

850
                payIntent.OutgoingChannelIDs = append(
×
851
                        payIntent.OutgoingChannelIDs, rpcPayReq.OutgoingChanId,
×
852
                )
×
853
        }
854

855
        // Pass along a last hop restriction if specified.
856
        if len(rpcPayReq.LastHopPubkey) > 0 {
23✔
857
                lastHop, err := route.NewVertexFromBytes(
1✔
858
                        rpcPayReq.LastHopPubkey,
1✔
859
                )
1✔
860
                if err != nil {
2✔
861
                        return nil, err
1✔
862
                }
1✔
863
                payIntent.LastHop = &lastHop
×
864
        }
865

866
        // Take the CLTV limit from the request if set, otherwise use the max.
867
        cltvLimit, err := ValidateCLTVLimit(
21✔
868
                uint32(rpcPayReq.CltvLimit), r.MaxTotalTimelock,
21✔
869
        )
21✔
870
        if err != nil {
22✔
871
                return nil, err
1✔
872
        }
1✔
873
        payIntent.CltvLimit = cltvLimit
20✔
874

20✔
875
        // Attempt to parse the max parts value set by the user, if this value
20✔
876
        // isn't set, then we'll use the current default value for this
20✔
877
        // setting.
20✔
878
        maxParts := rpcPayReq.MaxParts
20✔
879
        if maxParts == 0 {
37✔
880
                maxParts = DefaultMaxParts
17✔
881
        }
17✔
882
        payIntent.MaxParts = maxParts
20✔
883

20✔
884
        // If this payment had a max shard amount specified, then we'll apply
20✔
885
        // that now, which'll force us to always make payment splits smaller
20✔
886
        // than this.
20✔
887
        if rpcPayReq.MaxShardSizeMsat > 0 {
24✔
888
                shardAmtMsat := lnwire.MilliSatoshi(rpcPayReq.MaxShardSizeMsat)
4✔
889
                payIntent.MaxShardAmt = &shardAmtMsat
4✔
890

4✔
891
                // If the requested max_parts exceeds the allowed limit, then we
4✔
892
                // cannot send the payment amount.
4✔
893
                if payIntent.MaxParts > MaxPartsUpperLimit {
5✔
894
                        return nil, fmt.Errorf("requested max_parts (%v) "+
1✔
895
                                "exceeds the allowed upper limit of %v; cannot"+
1✔
896
                                " send payment amount with max_shard_size_msat"+
1✔
897
                                "=%v", payIntent.MaxParts, MaxPartsUpperLimit,
1✔
898
                                *payIntent.MaxShardAmt)
1✔
899
                }
1✔
900
        }
901

902
        // Take fee limit from request.
903
        payIntent.FeeLimit, err = lnrpc.UnmarshallAmt(
19✔
904
                rpcPayReq.FeeLimitSat, rpcPayReq.FeeLimitMsat,
19✔
905
        )
19✔
906
        if err != nil {
21✔
907
                return nil, err
2✔
908
        }
2✔
909

910
        customRecords := record.CustomSet(rpcPayReq.DestCustomRecords)
17✔
911
        if err := customRecords.Validate(); err != nil {
18✔
912
                return nil, err
1✔
913
        }
1✔
914
        payIntent.DestCustomRecords = customRecords
16✔
915

16✔
916
        // Keysend payments do not support MPP payments.
16✔
917
        //
16✔
918
        // NOTE: There is no need to validate the `MaxParts` value here because
16✔
919
        // it is set to 1 somewhere else in case it's a keysend payment.
16✔
920
        if customRecords.IsKeysend() {
17✔
921
                if payIntent.MaxShardAmt != nil {
2✔
922
                        return nil, errors.New("keysend payments cannot " +
1✔
923
                                "specify a max shard amount - MPP not " +
1✔
924
                                "supported with keysend payments")
1✔
925
                }
1✔
926
        }
927

928
        firstHopRecords := lnwire.CustomRecords(rpcPayReq.FirstHopCustomRecords)
15✔
929
        if err := firstHopRecords.Validate(); err != nil {
16✔
930
                return nil, err
1✔
931
        }
1✔
932
        payIntent.FirstHopCustomRecords = firstHopRecords
14✔
933

14✔
934
        // If the experimental endorsement signal is not already set, propagate
14✔
935
        // a zero value field if configured to set this signal.
14✔
936
        if r.ShouldSetExpEndorsement() {
15✔
937
                if payIntent.FirstHopCustomRecords == nil {
2✔
938
                        payIntent.FirstHopCustomRecords = make(
1✔
939
                                map[uint64][]byte,
1✔
940
                        )
1✔
941
                }
1✔
942

943
                t := uint64(lnwire.ExperimentalEndorsementType)
1✔
944
                if _, set := payIntent.FirstHopCustomRecords[t]; !set {
2✔
945
                        payIntent.FirstHopCustomRecords[t] = []byte{
1✔
946
                                lnwire.ExperimentalUnendorsed,
1✔
947
                        }
1✔
948
                }
1✔
949
        }
950

951
        payIntent.PayAttemptTimeout = time.Second *
14✔
952
                time.Duration(rpcPayReq.TimeoutSeconds)
14✔
953

14✔
954
        // Route hints.
14✔
955
        routeHints, err := unmarshallRouteHints(
14✔
956
                rpcPayReq.RouteHints,
14✔
957
        )
14✔
958
        if err != nil {
14✔
959
                return nil, err
×
960
        }
×
961
        payIntent.RouteHints = routeHints
14✔
962

14✔
963
        // Unmarshall either sat or msat amount from request.
14✔
964
        reqAmt, err := lnrpc.UnmarshallAmt(
14✔
965
                rpcPayReq.Amt, rpcPayReq.AmtMsat,
14✔
966
        )
14✔
967
        if err != nil {
15✔
968
                return nil, err
1✔
969
        }
1✔
970

971
        // If the payment request field isn't blank, then the details of the
972
        // invoice are encoded entirely within the encoded payReq.  So we'll
973
        // attempt to decode it, populating the payment accordingly.
974
        if rpcPayReq.PaymentRequest != "" {
19✔
975
                switch {
6✔
976

977
                case len(rpcPayReq.Dest) > 0:
1✔
978
                        return nil, errors.New("dest and payment_request " +
1✔
979
                                "cannot appear together")
1✔
980

981
                case len(rpcPayReq.PaymentHash) > 0:
1✔
982
                        return nil, errors.New("payment_hash and payment_request " +
1✔
983
                                "cannot appear together")
1✔
984

985
                case rpcPayReq.FinalCltvDelta != 0:
1✔
986
                        return nil, errors.New("final_cltv_delta and payment_request " +
1✔
987
                                "cannot appear together")
1✔
988
                }
989

990
                payReq, err := zpay32.Decode(
3✔
991
                        rpcPayReq.PaymentRequest, r.ActiveNetParams,
3✔
992
                )
3✔
993
                if err != nil {
4✔
994
                        return nil, err
1✔
995
                }
1✔
996

997
                // Next, we'll ensure that this payreq hasn't already expired.
998
                err = ValidatePayReqExpiry(r.Clock, payReq)
2✔
999
                if err != nil {
3✔
1000
                        return nil, err
1✔
1001
                }
1✔
1002

1003
                // An invoice must include either a payment address or
1004
                // blinded paths.
1005
                if payReq.PaymentAddr.IsNone() &&
1✔
1006
                        len(payReq.BlindedPaymentPaths) == 0 {
2✔
1007

1✔
1008
                        return nil, errors.New("payment request must contain " +
1✔
1009
                                "either a payment address or blinded paths")
1✔
1010
                }
1✔
1011

1012
                // If the amount was not included in the invoice, then we let
1013
                // the payer specify the amount of satoshis they wish to send.
1014
                // We override the amount to pay with the amount provided from
1015
                // the payment request.
UNCOV
1016
                if payReq.MilliSat == nil {
×
1017
                        if reqAmt == 0 {
×
1018
                                return nil, errors.New("amount must be " +
×
1019
                                        "specified when paying a zero amount " +
×
1020
                                        "invoice")
×
1021
                        }
×
1022

1023
                        payIntent.Amount = reqAmt
×
UNCOV
1024
                } else {
×
UNCOV
1025
                        if reqAmt != 0 {
×
1026
                                return nil, errors.New("amount must not be " +
×
1027
                                        "specified when paying a non-zero " +
×
1028
                                        "amount invoice")
×
1029
                        }
×
1030

UNCOV
1031
                        payIntent.Amount = *payReq.MilliSat
×
1032
                }
1033

UNCOV
1034
                if !payReq.Features.HasFeature(lnwire.MPPOptional) &&
×
UNCOV
1035
                        !payReq.Features.HasFeature(lnwire.AMPOptional) {
×
1036

×
1037
                        payIntent.MaxParts = 1
×
1038
                }
×
1039

UNCOV
1040
                payAddr := payReq.PaymentAddr
×
UNCOV
1041
                if payReq.Features.HasFeature(lnwire.AMPOptional) {
×
UNCOV
1042
                        // The opt-in AMP flag is required to pay an AMP
×
UNCOV
1043
                        // invoice.
×
UNCOV
1044
                        if !rpcPayReq.Amp {
×
1045
                                return nil, fmt.Errorf("the AMP flag (--amp " +
×
1046
                                        "or SendPaymentRequest.Amp) must be " +
×
1047
                                        "set to pay an AMP invoice")
×
1048
                        }
×
1049

1050
                        // Generate random SetID and root share.
UNCOV
1051
                        var setID [32]byte
×
UNCOV
1052
                        _, err = rand.Read(setID[:])
×
UNCOV
1053
                        if err != nil {
×
1054
                                return nil, err
×
1055
                        }
×
1056

UNCOV
1057
                        var rootShare [32]byte
×
UNCOV
1058
                        _, err = rand.Read(rootShare[:])
×
UNCOV
1059
                        if err != nil {
×
1060
                                return nil, err
×
1061
                        }
×
UNCOV
1062
                        err := payIntent.SetAMP(&routing.AMPOptions{
×
UNCOV
1063
                                SetID:     setID,
×
UNCOV
1064
                                RootShare: rootShare,
×
UNCOV
1065
                        })
×
UNCOV
1066
                        if err != nil {
×
1067
                                return nil, err
×
1068
                        }
×
1069

1070
                        // For AMP invoices, we'll allow users to override the
1071
                        // included payment addr to allow the invoice to be
1072
                        // pseudo-reusable, e.g. the invoice parameters are
1073
                        // reused (amt, cltv, hop hints, etc) even though the
1074
                        // payments will share different payment hashes.
1075
                        //
1076
                        // NOTE: This will only work when the peer has
1077
                        // spontaneous AMP payments enabled.
UNCOV
1078
                        if len(rpcPayReq.PaymentAddr) > 0 {
×
UNCOV
1079
                                var addr [32]byte
×
UNCOV
1080
                                copy(addr[:], rpcPayReq.PaymentAddr)
×
UNCOV
1081
                                payAddr = fn.Some(addr)
×
UNCOV
1082
                        }
×
UNCOV
1083
                } else {
×
UNCOV
1084
                        err = payIntent.SetPaymentHash(*payReq.PaymentHash)
×
UNCOV
1085
                        if err != nil {
×
1086
                                return nil, err
×
1087
                        }
×
1088
                }
1089

UNCOV
1090
                destKey := payReq.Destination.SerializeCompressed()
×
UNCOV
1091
                copy(payIntent.Target[:], destKey)
×
UNCOV
1092

×
UNCOV
1093
                payIntent.FinalCLTVDelta = uint16(payReq.MinFinalCLTVExpiry())
×
UNCOV
1094
                payIntent.RouteHints = append(
×
UNCOV
1095
                        payIntent.RouteHints, payReq.RouteHints...,
×
UNCOV
1096
                )
×
UNCOV
1097
                payIntent.DestFeatures = payReq.Features
×
UNCOV
1098
                payIntent.PaymentAddr = payAddr
×
UNCOV
1099
                payIntent.PaymentRequest = []byte(rpcPayReq.PaymentRequest)
×
UNCOV
1100
                payIntent.Metadata = payReq.Metadata
×
UNCOV
1101

×
UNCOV
1102
                if len(payReq.BlindedPaymentPaths) > 0 {
×
UNCOV
1103
                        pathSet, err := BuildBlindedPathSet(
×
UNCOV
1104
                                payReq.BlindedPaymentPaths,
×
UNCOV
1105
                        )
×
UNCOV
1106
                        if err != nil {
×
1107
                                return nil, err
×
1108
                        }
×
UNCOV
1109
                        payIntent.BlindedPathSet = pathSet
×
UNCOV
1110

×
UNCOV
1111
                        // Replace the target node with the target public key
×
UNCOV
1112
                        // of the blinded path set.
×
UNCOV
1113
                        copy(
×
UNCOV
1114
                                payIntent.Target[:],
×
UNCOV
1115
                                pathSet.TargetPubKey().SerializeCompressed(),
×
UNCOV
1116
                        )
×
UNCOV
1117

×
UNCOV
1118
                        pathFeatures := pathSet.Features()
×
UNCOV
1119
                        if !pathFeatures.IsEmpty() {
×
1120
                                payIntent.DestFeatures = pathFeatures.Clone()
×
1121
                        }
×
1122
                }
1123
        } else {
7✔
1124
                // Otherwise, If the payment request field was not specified
7✔
1125
                // (and a custom route wasn't specified), construct the payment
7✔
1126
                // from the other fields.
7✔
1127

7✔
1128
                // Payment destination.
7✔
1129
                target, err := route.NewVertexFromBytes(rpcPayReq.Dest)
7✔
1130
                if err != nil {
8✔
1131
                        return nil, err
1✔
1132
                }
1✔
1133
                payIntent.Target = target
6✔
1134

6✔
1135
                // Final payment CLTV delta.
6✔
1136
                if rpcPayReq.FinalCltvDelta != 0 {
7✔
1137
                        payIntent.FinalCLTVDelta =
1✔
1138
                                uint16(rpcPayReq.FinalCltvDelta)
1✔
1139
                } else {
6✔
1140
                        payIntent.FinalCLTVDelta = r.DefaultFinalCltvDelta
5✔
1141
                }
5✔
1142

1143
                // Amount.
1144
                if reqAmt == 0 {
7✔
1145
                        return nil, errors.New("amount must be specified")
1✔
1146
                }
1✔
1147

1148
                payIntent.Amount = reqAmt
5✔
1149

5✔
1150
                // Parse destination feature bits.
5✔
1151
                features, err := UnmarshalFeatures(rpcPayReq.DestFeatures)
5✔
1152
                if err != nil {
5✔
1153
                        return nil, err
×
1154
                }
×
1155

1156
                // Validate the features if any was specified.
1157
                if features != nil {
6✔
1158
                        err = feature.ValidateDeps(features)
1✔
1159
                        if err != nil {
1✔
1160
                                return nil, err
×
1161
                        }
×
1162
                }
1163

1164
                // If this is an AMP payment, we must generate the initial
1165
                // randomness.
1166
                if rpcPayReq.Amp {
6✔
1167
                        // If no destination features were specified, we set
1✔
1168
                        // those necessary for AMP payments.
1✔
1169
                        if features == nil {
1✔
UNCOV
1170
                                ampFeatures := []lnrpc.FeatureBit{
×
UNCOV
1171
                                        lnrpc.FeatureBit_TLV_ONION_OPT,
×
UNCOV
1172
                                        lnrpc.FeatureBit_PAYMENT_ADDR_OPT,
×
UNCOV
1173
                                        lnrpc.FeatureBit_AMP_OPT,
×
UNCOV
1174
                                }
×
UNCOV
1175

×
UNCOV
1176
                                features, err = UnmarshalFeatures(ampFeatures)
×
UNCOV
1177
                                if err != nil {
×
1178
                                        return nil, err
×
1179
                                }
×
1180
                        }
1181

1182
                        // First make sure the destination supports AMP.
1183
                        if !features.HasFeature(lnwire.AMPOptional) {
2✔
1184
                                return nil, fmt.Errorf("destination doesn't " +
1✔
1185
                                        "support AMP payments")
1✔
1186
                        }
1✔
1187

1188
                        // If no payment address is set, generate a random one.
UNCOV
1189
                        var payAddr [32]byte
×
UNCOV
1190
                        if len(rpcPayReq.PaymentAddr) == 0 {
×
UNCOV
1191
                                _, err = rand.Read(payAddr[:])
×
UNCOV
1192
                                if err != nil {
×
1193
                                        return nil, err
×
1194
                                }
×
1195
                        } else {
×
1196
                                copy(payAddr[:], rpcPayReq.PaymentAddr)
×
1197
                        }
×
UNCOV
1198
                        payIntent.PaymentAddr = fn.Some(payAddr)
×
UNCOV
1199

×
UNCOV
1200
                        // Generate random SetID and root share.
×
UNCOV
1201
                        var setID [32]byte
×
UNCOV
1202
                        _, err = rand.Read(setID[:])
×
UNCOV
1203
                        if err != nil {
×
1204
                                return nil, err
×
1205
                        }
×
1206

UNCOV
1207
                        var rootShare [32]byte
×
UNCOV
1208
                        _, err = rand.Read(rootShare[:])
×
UNCOV
1209
                        if err != nil {
×
1210
                                return nil, err
×
1211
                        }
×
UNCOV
1212
                        err := payIntent.SetAMP(&routing.AMPOptions{
×
UNCOV
1213
                                SetID:     setID,
×
UNCOV
1214
                                RootShare: rootShare,
×
UNCOV
1215
                        })
×
UNCOV
1216
                        if err != nil {
×
1217
                                return nil, err
×
1218
                        }
×
1219
                } else {
4✔
1220
                        // Payment hash.
4✔
1221
                        paymentHash, err := lntypes.MakeHash(rpcPayReq.PaymentHash)
4✔
1222
                        if err != nil {
5✔
1223
                                return nil, err
1✔
1224
                        }
1✔
1225

1226
                        err = payIntent.SetPaymentHash(paymentHash)
3✔
1227
                        if err != nil {
3✔
1228
                                return nil, err
×
1229
                        }
×
1230

1231
                        // If the payment addresses is specified, then we'll
1232
                        // also populate that now as well.
1233
                        if len(rpcPayReq.PaymentAddr) != 0 {
3✔
1234
                                var payAddr [32]byte
×
1235
                                copy(payAddr[:], rpcPayReq.PaymentAddr)
×
1236

×
1237
                                payIntent.PaymentAddr = fn.Some(payAddr)
×
1238
                        }
×
1239
                }
1240

1241
                payIntent.DestFeatures = features
3✔
1242
        }
1243

1244
        // Validate that the MPP parameters are compatible with the
1245
        // payment amount. In other words, the parameters are invalid if
1246
        // they do not permit sending the full payment amount.
1247
        if payIntent.MaxShardAmt != nil {
5✔
1248
                maxPossibleAmount := (*payIntent.MaxShardAmt) *
2✔
1249
                        lnwire.MilliSatoshi(payIntent.MaxParts)
2✔
1250

2✔
1251
                if payIntent.Amount > maxPossibleAmount {
3✔
1252
                        return nil, fmt.Errorf("payment amount %v exceeds "+
1✔
1253
                                "maximum possible amount %v with max_parts=%v "+
1✔
1254
                                "and max_shard_size_msat=%v", payIntent.Amount,
1✔
1255
                                maxPossibleAmount, payIntent.MaxParts,
1✔
1256
                                *payIntent.MaxShardAmt,
1✔
1257
                        )
1✔
1258
                }
1✔
1259
        }
1260

1261
        // Do bounds checking with the block padding so the router isn't
1262
        // left with a zombie payment in case the user messes up.
1263
        err = routing.ValidateCLTVLimit(
2✔
1264
                payIntent.CltvLimit, payIntent.FinalCLTVDelta, true,
2✔
1265
        )
2✔
1266
        if err != nil {
2✔
1267
                return nil, err
×
1268
        }
×
1269

1270
        // Check for disallowed payments to self.
1271
        if !rpcPayReq.AllowSelfPayment && payIntent.Target == r.SelfNode {
3✔
1272
                return nil, errors.New("self-payments not allowed")
1✔
1273
        }
1✔
1274

1275
        return payIntent, nil
1✔
1276
}
1277

1278
// BuildBlindedPathSet marshals a set of zpay32.BlindedPaymentPath and uses
1279
// the result to build a new routing.BlindedPaymentPathSet.
1280
func BuildBlindedPathSet(paths []*zpay32.BlindedPaymentPath) (
UNCOV
1281
        *routing.BlindedPaymentPathSet, error) {
×
UNCOV
1282

×
UNCOV
1283
        marshalledPaths := make([]*routing.BlindedPayment, len(paths))
×
UNCOV
1284
        for i, path := range paths {
×
UNCOV
1285
                paymentPath := marshalBlindedPayment(path)
×
UNCOV
1286

×
UNCOV
1287
                err := paymentPath.Validate()
×
UNCOV
1288
                if err != nil {
×
1289
                        return nil, err
×
1290
                }
×
1291

UNCOV
1292
                marshalledPaths[i] = paymentPath
×
1293
        }
1294

UNCOV
1295
        return routing.NewBlindedPaymentPathSet(marshalledPaths)
×
1296
}
1297

1298
// marshalBlindedPayment marshals a zpay32.BLindedPaymentPath into a
1299
// routing.BlindedPayment.
1300
func marshalBlindedPayment(
UNCOV
1301
        path *zpay32.BlindedPaymentPath) *routing.BlindedPayment {
×
UNCOV
1302

×
UNCOV
1303
        return &routing.BlindedPayment{
×
UNCOV
1304
                BlindedPath: &sphinx.BlindedPath{
×
UNCOV
1305
                        IntroductionPoint: path.Hops[0].BlindedNodePub,
×
UNCOV
1306
                        BlindingPoint:     path.FirstEphemeralBlindingPoint,
×
UNCOV
1307
                        BlindedHops:       path.Hops,
×
UNCOV
1308
                },
×
UNCOV
1309
                BaseFee:             path.FeeBaseMsat,
×
UNCOV
1310
                ProportionalFeeRate: path.FeeRate,
×
UNCOV
1311
                CltvExpiryDelta:     path.CltvExpiryDelta,
×
UNCOV
1312
                HtlcMinimum:         path.HTLCMinMsat,
×
UNCOV
1313
                HtlcMaximum:         path.HTLCMaxMsat,
×
UNCOV
1314
                Features:            path.Features,
×
UNCOV
1315
        }
×
UNCOV
1316
}
×
1317

1318
// unmarshallRouteHints unmarshalls a list of route hints.
1319
func unmarshallRouteHints(rpcRouteHints []*lnrpc.RouteHint) (
1320
        [][]zpay32.HopHint, error) {
20✔
1321

20✔
1322
        routeHints := make([][]zpay32.HopHint, 0, len(rpcRouteHints))
20✔
1323
        for _, rpcRouteHint := range rpcRouteHints {
26✔
1324
                routeHint := make(
6✔
1325
                        []zpay32.HopHint, 0, len(rpcRouteHint.HopHints),
6✔
1326
                )
6✔
1327
                for _, rpcHint := range rpcRouteHint.HopHints {
12✔
1328
                        hint, err := unmarshallHopHint(rpcHint)
6✔
1329
                        if err != nil {
6✔
1330
                                return nil, err
×
1331
                        }
×
1332

1333
                        routeHint = append(routeHint, hint)
6✔
1334
                }
1335
                routeHints = append(routeHints, routeHint)
6✔
1336
        }
1337

1338
        return routeHints, nil
20✔
1339
}
1340

1341
// unmarshallHopHint unmarshalls a single hop hint.
1342
func unmarshallHopHint(rpcHint *lnrpc.HopHint) (zpay32.HopHint, error) {
6✔
1343
        pubBytes, err := hex.DecodeString(rpcHint.NodeId)
6✔
1344
        if err != nil {
6✔
1345
                return zpay32.HopHint{}, err
×
1346
        }
×
1347

1348
        pubkey, err := btcec.ParsePubKey(pubBytes)
6✔
1349
        if err != nil {
6✔
1350
                return zpay32.HopHint{}, err
×
1351
        }
×
1352

1353
        return zpay32.HopHint{
6✔
1354
                NodeID:                    pubkey,
6✔
1355
                ChannelID:                 rpcHint.ChanId,
6✔
1356
                FeeBaseMSat:               rpcHint.FeeBaseMsat,
6✔
1357
                FeeProportionalMillionths: rpcHint.FeeProportionalMillionths,
6✔
1358
                CLTVExpiryDelta:           uint16(rpcHint.CltvExpiryDelta),
6✔
1359
        }, nil
6✔
1360
}
1361

1362
// MarshalFeatures converts a feature vector into a list of uint32's.
UNCOV
1363
func MarshalFeatures(feats *lnwire.FeatureVector) []lnrpc.FeatureBit {
×
UNCOV
1364
        var featureBits []lnrpc.FeatureBit
×
UNCOV
1365
        for feature := range feats.Features() {
×
UNCOV
1366
                featureBits = append(featureBits, lnrpc.FeatureBit(feature))
×
UNCOV
1367
        }
×
1368

UNCOV
1369
        return featureBits
×
1370
}
1371

1372
// UnmarshalFeatures converts a list of uint32's into a valid feature vector.
1373
// This method checks that feature bit pairs aren't assigned together, and
1374
// validates transitive dependencies.
1375
func UnmarshalFeatures(
1376
        rpcFeatures []lnrpc.FeatureBit) (*lnwire.FeatureVector, error) {
10✔
1377

10✔
1378
        // If no destination features are specified we'll return nil to signal
10✔
1379
        // that the router should try to use the graph as a fallback.
10✔
1380
        if rpcFeatures == nil {
14✔
1381
                return nil, nil
4✔
1382
        }
4✔
1383

1384
        raw := lnwire.NewRawFeatureVector()
6✔
1385
        for _, bit := range rpcFeatures {
11✔
1386
                err := raw.SafeSet(lnwire.FeatureBit(bit))
5✔
1387
                if err != nil {
5✔
1388
                        return nil, err
×
1389
                }
×
1390
        }
1391

1392
        return lnwire.NewFeatureVector(raw, lnwire.Features), nil
6✔
1393
}
1394

1395
// ValidatePayReqExpiry checks if the passed payment request has expired. In
1396
// the case it has expired, an error will be returned.
1397
func ValidatePayReqExpiry(clock clock.Clock, payReq *zpay32.Invoice) error {
2✔
1398
        expiry := payReq.Expiry()
2✔
1399
        validUntil := payReq.Timestamp.Add(expiry)
2✔
1400
        if clock.Now().After(validUntil) {
3✔
1401
                return fmt.Errorf("invoice expired. Valid until %v", validUntil)
1✔
1402
        }
1✔
1403

1404
        return nil
1✔
1405
}
1406

1407
// ValidateCLTVLimit returns a valid CLTV limit given a value and a maximum. If
1408
// the value exceeds the maximum, then an error is returned. If the value is 0,
1409
// then the maximum is used.
1410
func ValidateCLTVLimit(val, max uint32) (uint32, error) {
27✔
1411
        switch {
27✔
1412
        case val == 0:
26✔
1413
                return max, nil
26✔
1414
        case val > max:
1✔
1415
                return 0, fmt.Errorf("total time lock of %v exceeds max "+
1✔
1416
                        "allowed %v", val, max)
1✔
1417
        default:
×
1418
                return val, nil
×
1419
        }
1420
}
1421

1422
// UnmarshalMPP accepts the mpp_total_amt_msat and mpp_payment_addr fields from
1423
// an RPC request and converts into an record.MPP object. An error is returned
1424
// if the payment address is not 0 or 32 bytes. If the total amount and payment
1425
// address are zero-value, the return value will be nil signaling there is no
1426
// MPP record to attach to this hop. Otherwise, a non-nil reocrd will be
1427
// contained combining the provided values.
1428
func UnmarshalMPP(reqMPP *lnrpc.MPPRecord) (*record.MPP, error) {
6✔
1429
        // If no MPP record was submitted, assume the user wants to send a
6✔
1430
        // regular payment.
6✔
1431
        if reqMPP == nil {
7✔
1432
                return nil, nil
1✔
1433
        }
1✔
1434

1435
        reqTotal := reqMPP.TotalAmtMsat
5✔
1436
        reqAddr := reqMPP.PaymentAddr
5✔
1437

5✔
1438
        switch {
5✔
1439
        // No MPP fields were provided.
1440
        case reqTotal == 0 && len(reqAddr) == 0:
1✔
1441
                return nil, fmt.Errorf("missing total_msat and payment_addr")
1✔
1442

1443
        // Total is present, but payment address is missing.
1444
        case reqTotal > 0 && len(reqAddr) == 0:
1✔
1445
                return nil, fmt.Errorf("missing payment_addr")
1✔
1446

1447
        // Payment address is present, but total is missing.
1448
        case reqTotal == 0 && len(reqAddr) > 0:
1✔
1449
                return nil, fmt.Errorf("missing total_msat")
1✔
1450
        }
1451

1452
        addr, err := lntypes.MakeHash(reqAddr)
2✔
1453
        if err != nil {
3✔
1454
                return nil, fmt.Errorf("unable to parse "+
1✔
1455
                        "payment_addr: %v", err)
1✔
1456
        }
1✔
1457

1458
        total := lnwire.MilliSatoshi(reqTotal)
1✔
1459

1✔
1460
        return record.NewMPP(total, addr), nil
1✔
1461
}
1462

1463
func UnmarshalAMP(reqAMP *lnrpc.AMPRecord) (*record.AMP, error) {
5✔
1464
        if reqAMP == nil {
6✔
1465
                return nil, nil
1✔
1466
        }
1✔
1467

1468
        reqRootShare := reqAMP.RootShare
4✔
1469
        reqSetID := reqAMP.SetId
4✔
1470

4✔
1471
        switch {
4✔
1472
        case len(reqRootShare) != 32:
2✔
1473
                return nil, errors.New("AMP root_share must be 32 bytes")
2✔
1474

1475
        case len(reqSetID) != 32:
1✔
1476
                return nil, errors.New("AMP set_id must be 32 bytes")
1✔
1477
        }
1478

1479
        var (
1✔
1480
                rootShare [32]byte
1✔
1481
                setID     [32]byte
1✔
1482
        )
1✔
1483
        copy(rootShare[:], reqRootShare)
1✔
1484
        copy(setID[:], reqSetID)
1✔
1485

1✔
1486
        return record.NewAMP(rootShare, setID, reqAMP.ChildIndex), nil
1✔
1487
}
1488

1489
// MarshalHTLCAttempt constructs an RPC HTLCAttempt from the db representation.
1490
func (r *RouterBackend) MarshalHTLCAttempt(
UNCOV
1491
        htlc paymentsdb.HTLCAttempt) (*lnrpc.HTLCAttempt, error) {
×
UNCOV
1492

×
UNCOV
1493
        route, err := r.MarshallRoute(&htlc.Route)
×
UNCOV
1494
        if err != nil {
×
1495
                return nil, err
×
1496
        }
×
1497

UNCOV
1498
        rpcAttempt := &lnrpc.HTLCAttempt{
×
UNCOV
1499
                AttemptId:     htlc.AttemptID,
×
UNCOV
1500
                AttemptTimeNs: MarshalTimeNano(htlc.AttemptTime),
×
UNCOV
1501
                Route:         route,
×
UNCOV
1502
        }
×
UNCOV
1503

×
UNCOV
1504
        switch {
×
UNCOV
1505
        case htlc.Settle != nil:
×
UNCOV
1506
                rpcAttempt.Status = lnrpc.HTLCAttempt_SUCCEEDED
×
UNCOV
1507
                rpcAttempt.ResolveTimeNs = MarshalTimeNano(
×
UNCOV
1508
                        htlc.Settle.SettleTime,
×
UNCOV
1509
                )
×
UNCOV
1510
                rpcAttempt.Preimage = htlc.Settle.Preimage[:]
×
1511

UNCOV
1512
        case htlc.Failure != nil:
×
UNCOV
1513
                rpcAttempt.Status = lnrpc.HTLCAttempt_FAILED
×
UNCOV
1514
                rpcAttempt.ResolveTimeNs = MarshalTimeNano(
×
UNCOV
1515
                        htlc.Failure.FailTime,
×
UNCOV
1516
                )
×
UNCOV
1517

×
UNCOV
1518
                var err error
×
UNCOV
1519
                rpcAttempt.Failure, err = marshallHtlcFailure(htlc.Failure)
×
UNCOV
1520
                if err != nil {
×
1521
                        return nil, err
×
1522
                }
×
UNCOV
1523
        default:
×
UNCOV
1524
                rpcAttempt.Status = lnrpc.HTLCAttempt_IN_FLIGHT
×
1525
        }
1526

UNCOV
1527
        return rpcAttempt, nil
×
1528
}
1529

1530
// marshallHtlcFailure marshalls htlc fail info from the database to its rpc
1531
// representation.
1532
func marshallHtlcFailure(failure *paymentsdb.HTLCFailInfo) (*lnrpc.Failure,
UNCOV
1533
        error) {
×
UNCOV
1534

×
UNCOV
1535
        rpcFailure := &lnrpc.Failure{
×
UNCOV
1536
                FailureSourceIndex: failure.FailureSourceIndex,
×
UNCOV
1537
        }
×
UNCOV
1538

×
UNCOV
1539
        switch failure.Reason {
×
1540
        case paymentsdb.HTLCFailUnknown:
×
1541
                rpcFailure.Code = lnrpc.Failure_UNKNOWN_FAILURE
×
1542

1543
        case paymentsdb.HTLCFailUnreadable:
×
1544
                rpcFailure.Code = lnrpc.Failure_UNREADABLE_FAILURE
×
1545

1546
        case paymentsdb.HTLCFailInternal:
×
1547
                rpcFailure.Code = lnrpc.Failure_INTERNAL_FAILURE
×
1548

UNCOV
1549
        case paymentsdb.HTLCFailMessage:
×
UNCOV
1550
                err := marshallWireError(failure.Message, rpcFailure)
×
UNCOV
1551
                if err != nil {
×
1552
                        return nil, err
×
1553
                }
×
1554

1555
        default:
×
1556
                return nil, errors.New("unknown htlc failure reason")
×
1557
        }
1558

UNCOV
1559
        return rpcFailure, nil
×
1560
}
1561

1562
// MarshalTimeNano converts a time.Time into its nanosecond representation. If
1563
// the time is zero, this method simply returns 0, since calling UnixNano() on a
1564
// zero-valued time is undefined.
1565
func MarshalTimeNano(t time.Time) int64 {
3✔
1566
        if t.IsZero() {
6✔
1567
                return 0
3✔
1568
        }
3✔
UNCOV
1569
        return t.UnixNano()
×
1570
}
1571

1572
// marshallError marshall an error as received from the switch to rpc structs
1573
// suitable for returning to the caller of an rpc method.
1574
//
1575
// Because of difficulties with using protobuf oneof constructs in some
1576
// languages, the decision was made here to use a single message format for all
1577
// failure messages with some fields left empty depending on the failure type.
UNCOV
1578
func marshallError(sendError error) (*lnrpc.Failure, error) {
×
UNCOV
1579
        response := &lnrpc.Failure{}
×
UNCOV
1580

×
UNCOV
1581
        if sendError == htlcswitch.ErrUnreadableFailureMessage {
×
1582
                response.Code = lnrpc.Failure_UNREADABLE_FAILURE
×
1583
                return response, nil
×
1584
        }
×
1585

UNCOV
1586
        rtErr, ok := sendError.(htlcswitch.ClearTextError)
×
UNCOV
1587
        if !ok {
×
1588
                return nil, sendError
×
1589
        }
×
1590

UNCOV
1591
        err := marshallWireError(rtErr.WireMessage(), response)
×
UNCOV
1592
        if err != nil {
×
1593
                return nil, err
×
1594
        }
×
1595

1596
        // If the ClearTextError received is a ForwardingError, the error
1597
        // originated from a node along the route, not locally on our outgoing
1598
        // link. We set failureSourceIdx to the index of the node where the
1599
        // failure occurred. If the error is not a ForwardingError, the failure
1600
        // occurred at our node, so we leave the index as 0 to indicate that
1601
        // we failed locally.
UNCOV
1602
        fErr, ok := rtErr.(*htlcswitch.ForwardingError)
×
UNCOV
1603
        if ok {
×
1604
                response.FailureSourceIndex = uint32(fErr.FailureSourceIdx)
×
1605
        }
×
1606

UNCOV
1607
        return response, nil
×
1608
}
1609

1610
// marshallError marshall an error as received from the switch to rpc structs
1611
// suitable for returning to the caller of an rpc method.
1612
//
1613
// Because of difficulties with using protobuf oneof constructs in some
1614
// languages, the decision was made here to use a single message format for all
1615
// failure messages with some fields left empty depending on the failure type.
1616
func marshallWireError(msg lnwire.FailureMessage,
UNCOV
1617
        response *lnrpc.Failure) error {
×
UNCOV
1618

×
UNCOV
1619
        switch onionErr := msg.(type) {
×
UNCOV
1620
        case *lnwire.FailIncorrectDetails:
×
UNCOV
1621
                response.Code = lnrpc.Failure_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS
×
UNCOV
1622
                response.Height = onionErr.Height()
×
1623

1624
        case *lnwire.FailIncorrectPaymentAmount:
×
1625
                response.Code = lnrpc.Failure_INCORRECT_PAYMENT_AMOUNT
×
1626

1627
        case *lnwire.FailFinalIncorrectCltvExpiry:
×
1628
                response.Code = lnrpc.Failure_FINAL_INCORRECT_CLTV_EXPIRY
×
1629
                response.CltvExpiry = onionErr.CltvExpiry
×
1630

1631
        case *lnwire.FailFinalIncorrectHtlcAmount:
×
1632
                response.Code = lnrpc.Failure_FINAL_INCORRECT_HTLC_AMOUNT
×
1633
                response.HtlcMsat = uint64(onionErr.IncomingHTLCAmount)
×
1634

1635
        case *lnwire.FailFinalExpiryTooSoon:
×
1636
                response.Code = lnrpc.Failure_FINAL_EXPIRY_TOO_SOON
×
1637

1638
        case *lnwire.FailInvalidRealm:
×
1639
                response.Code = lnrpc.Failure_INVALID_REALM
×
1640

1641
        case *lnwire.FailExpiryTooSoon:
×
1642
                response.Code = lnrpc.Failure_EXPIRY_TOO_SOON
×
1643
                response.ChannelUpdate = marshallChannelUpdate(&onionErr.Update)
×
1644

1645
        case *lnwire.FailExpiryTooFar:
×
1646
                response.Code = lnrpc.Failure_EXPIRY_TOO_FAR
×
1647

UNCOV
1648
        case *lnwire.FailInvalidOnionVersion:
×
UNCOV
1649
                response.Code = lnrpc.Failure_INVALID_ONION_VERSION
×
UNCOV
1650
                response.OnionSha_256 = onionErr.OnionSHA256[:]
×
1651

1652
        case *lnwire.FailInvalidOnionHmac:
×
1653
                response.Code = lnrpc.Failure_INVALID_ONION_HMAC
×
1654
                response.OnionSha_256 = onionErr.OnionSHA256[:]
×
1655

1656
        case *lnwire.FailInvalidOnionKey:
×
1657
                response.Code = lnrpc.Failure_INVALID_ONION_KEY
×
1658
                response.OnionSha_256 = onionErr.OnionSHA256[:]
×
1659

1660
        case *lnwire.FailAmountBelowMinimum:
×
1661
                response.Code = lnrpc.Failure_AMOUNT_BELOW_MINIMUM
×
1662
                response.ChannelUpdate = marshallChannelUpdate(&onionErr.Update)
×
1663
                response.HtlcMsat = uint64(onionErr.HtlcMsat)
×
1664

UNCOV
1665
        case *lnwire.FailFeeInsufficient:
×
UNCOV
1666
                response.Code = lnrpc.Failure_FEE_INSUFFICIENT
×
UNCOV
1667
                response.ChannelUpdate = marshallChannelUpdate(&onionErr.Update)
×
UNCOV
1668
                response.HtlcMsat = uint64(onionErr.HtlcMsat)
×
1669

1670
        case *lnwire.FailIncorrectCltvExpiry:
×
1671
                response.Code = lnrpc.Failure_INCORRECT_CLTV_EXPIRY
×
1672
                response.ChannelUpdate = marshallChannelUpdate(&onionErr.Update)
×
1673
                response.CltvExpiry = onionErr.CltvExpiry
×
1674

UNCOV
1675
        case *lnwire.FailChannelDisabled:
×
UNCOV
1676
                response.Code = lnrpc.Failure_CHANNEL_DISABLED
×
UNCOV
1677
                response.ChannelUpdate = marshallChannelUpdate(&onionErr.Update)
×
UNCOV
1678
                response.Flags = uint32(onionErr.Flags)
×
1679

UNCOV
1680
        case *lnwire.FailTemporaryChannelFailure:
×
UNCOV
1681
                response.Code = lnrpc.Failure_TEMPORARY_CHANNEL_FAILURE
×
UNCOV
1682
                response.ChannelUpdate = marshallChannelUpdate(onionErr.Update)
×
1683

1684
        case *lnwire.FailRequiredNodeFeatureMissing:
×
1685
                response.Code = lnrpc.Failure_REQUIRED_NODE_FEATURE_MISSING
×
1686

1687
        case *lnwire.FailRequiredChannelFeatureMissing:
×
1688
                response.Code = lnrpc.Failure_REQUIRED_CHANNEL_FEATURE_MISSING
×
1689

UNCOV
1690
        case *lnwire.FailUnknownNextPeer:
×
UNCOV
1691
                response.Code = lnrpc.Failure_UNKNOWN_NEXT_PEER
×
1692

1693
        case *lnwire.FailTemporaryNodeFailure:
×
1694
                response.Code = lnrpc.Failure_TEMPORARY_NODE_FAILURE
×
1695

1696
        case *lnwire.FailPermanentNodeFailure:
×
1697
                response.Code = lnrpc.Failure_PERMANENT_NODE_FAILURE
×
1698

UNCOV
1699
        case *lnwire.FailPermanentChannelFailure:
×
UNCOV
1700
                response.Code = lnrpc.Failure_PERMANENT_CHANNEL_FAILURE
×
1701

1702
        case *lnwire.FailMPPTimeout:
×
1703
                response.Code = lnrpc.Failure_MPP_TIMEOUT
×
1704

UNCOV
1705
        case *lnwire.InvalidOnionPayload:
×
UNCOV
1706
                response.Code = lnrpc.Failure_INVALID_ONION_PAYLOAD
×
1707

UNCOV
1708
        case *lnwire.FailInvalidBlinding:
×
UNCOV
1709
                response.Code = lnrpc.Failure_INVALID_ONION_BLINDING
×
UNCOV
1710
                response.OnionSha_256 = onionErr.OnionSHA256[:]
×
1711

1712
        case nil:
×
1713
                response.Code = lnrpc.Failure_UNKNOWN_FAILURE
×
1714

1715
        default:
×
1716
                return fmt.Errorf("cannot marshall failure %T", onionErr)
×
1717
        }
1718

UNCOV
1719
        return nil
×
1720
}
1721

1722
// marshallChannelUpdate marshalls a channel update as received over the wire to
1723
// the router rpc format.
UNCOV
1724
func marshallChannelUpdate(update *lnwire.ChannelUpdate1) *lnrpc.ChannelUpdate {
×
UNCOV
1725
        if update == nil {
×
1726
                return nil
×
1727
        }
×
1728

UNCOV
1729
        return &lnrpc.ChannelUpdate{
×
UNCOV
1730
                Signature:       update.Signature.RawBytes(),
×
UNCOV
1731
                ChainHash:       update.ChainHash[:],
×
UNCOV
1732
                ChanId:          update.ShortChannelID.ToUint64(),
×
UNCOV
1733
                Timestamp:       update.Timestamp,
×
UNCOV
1734
                MessageFlags:    uint32(update.MessageFlags),
×
UNCOV
1735
                ChannelFlags:    uint32(update.ChannelFlags),
×
UNCOV
1736
                TimeLockDelta:   uint32(update.TimeLockDelta),
×
UNCOV
1737
                HtlcMinimumMsat: uint64(update.HtlcMinimumMsat),
×
UNCOV
1738
                BaseFee:         update.BaseFee,
×
UNCOV
1739
                FeeRate:         update.FeeRate,
×
UNCOV
1740
                HtlcMaximumMsat: uint64(update.HtlcMaximumMsat),
×
UNCOV
1741
                ExtraOpaqueData: update.ExtraOpaqueData,
×
UNCOV
1742
        }
×
1743
}
1744

1745
// MarshallPayment marshall a payment to its rpc representation.
1746
func (r *RouterBackend) MarshallPayment(payment *paymentsdb.MPPayment) (
1747
        *lnrpc.Payment, error) {
3✔
1748

3✔
1749
        // Fetch the payment's preimage and the total paid in fees.
3✔
1750
        var (
3✔
1751
                fee      lnwire.MilliSatoshi
3✔
1752
                preimage lntypes.Preimage
3✔
1753
        )
3✔
1754
        for _, htlc := range payment.HTLCs {
3✔
UNCOV
1755
                // If any of the htlcs have settled, extract a valid
×
UNCOV
1756
                // preimage.
×
UNCOV
1757
                if htlc.Settle != nil {
×
UNCOV
1758
                        preimage = htlc.Settle.Preimage
×
UNCOV
1759
                        fee += htlc.Route.TotalFees()
×
UNCOV
1760
                }
×
1761
        }
1762

1763
        msatValue := int64(payment.Info.Value)
3✔
1764
        satValue := int64(payment.Info.Value.ToSatoshis())
3✔
1765

3✔
1766
        status, err := convertPaymentStatus(
3✔
1767
                payment.Status, r.UseStatusInitiated,
3✔
1768
        )
3✔
1769
        if err != nil {
3✔
1770
                return nil, err
×
1771
        }
×
1772

1773
        htlcs := make([]*lnrpc.HTLCAttempt, 0, len(payment.HTLCs))
3✔
1774
        for _, dbHTLC := range payment.HTLCs {
3✔
UNCOV
1775
                htlc, err := r.MarshalHTLCAttempt(dbHTLC)
×
UNCOV
1776
                if err != nil {
×
1777
                        return nil, err
×
1778
                }
×
1779

UNCOV
1780
                htlcs = append(htlcs, htlc)
×
1781
        }
1782

1783
        paymentID := payment.Info.PaymentIdentifier
3✔
1784
        creationTimeNS := MarshalTimeNano(payment.Info.CreationTime)
3✔
1785

3✔
1786
        failureReason, err := marshallPaymentFailureReason(
3✔
1787
                payment.FailureReason,
3✔
1788
        )
3✔
1789
        if err != nil {
3✔
1790
                return nil, err
×
1791
        }
×
1792

1793
        return &lnrpc.Payment{
3✔
1794
                // TODO: set this to setID for AMP-payments?
3✔
1795
                PaymentHash:           hex.EncodeToString(paymentID[:]),
3✔
1796
                Value:                 satValue,
3✔
1797
                ValueMsat:             msatValue,
3✔
1798
                ValueSat:              satValue,
3✔
1799
                CreationDate:          payment.Info.CreationTime.Unix(),
3✔
1800
                CreationTimeNs:        creationTimeNS,
3✔
1801
                Fee:                   int64(fee.ToSatoshis()),
3✔
1802
                FeeSat:                int64(fee.ToSatoshis()),
3✔
1803
                FeeMsat:               int64(fee),
3✔
1804
                PaymentPreimage:       hex.EncodeToString(preimage[:]),
3✔
1805
                PaymentRequest:        string(payment.Info.PaymentRequest),
3✔
1806
                Status:                status,
3✔
1807
                Htlcs:                 htlcs,
3✔
1808
                PaymentIndex:          payment.SequenceNum,
3✔
1809
                FailureReason:         failureReason,
3✔
1810
                FirstHopCustomRecords: payment.Info.FirstHopCustomRecords,
3✔
1811
        }, nil
3✔
1812
}
1813

1814
// convertPaymentStatus converts a channeldb.PaymentStatus to the type expected
1815
// by the RPC.
1816
func convertPaymentStatus(dbStatus paymentsdb.PaymentStatus, useInit bool) (
1817
        lnrpc.Payment_PaymentStatus, error) {
3✔
1818

3✔
1819
        switch dbStatus {
3✔
UNCOV
1820
        case paymentsdb.StatusInitiated:
×
UNCOV
1821
                // If the client understands the new status, return it.
×
UNCOV
1822
                if useInit {
×
UNCOV
1823
                        return lnrpc.Payment_INITIATED, nil
×
UNCOV
1824
                }
×
1825

1826
                // Otherwise remain the old behavior.
UNCOV
1827
                return lnrpc.Payment_IN_FLIGHT, nil
×
1828

1829
        case paymentsdb.StatusInFlight:
1✔
1830
                return lnrpc.Payment_IN_FLIGHT, nil
1✔
1831

1832
        case paymentsdb.StatusSucceeded:
2✔
1833
                return lnrpc.Payment_SUCCEEDED, nil
2✔
1834

UNCOV
1835
        case paymentsdb.StatusFailed:
×
UNCOV
1836
                return lnrpc.Payment_FAILED, nil
×
1837

1838
        default:
×
1839
                return 0, fmt.Errorf("unhandled payment status %v", dbStatus)
×
1840
        }
1841
}
1842

1843
// marshallPaymentFailureReason marshalls the failure reason to the corresponding rpc
1844
// type.
1845
func marshallPaymentFailureReason(reason *paymentsdb.FailureReason) (
1846
        lnrpc.PaymentFailureReason, error) {
3✔
1847

3✔
1848
        if reason == nil {
6✔
1849
                return lnrpc.PaymentFailureReason_FAILURE_REASON_NONE, nil
3✔
1850
        }
3✔
1851

UNCOV
1852
        switch *reason {
×
1853
        case paymentsdb.FailureReasonTimeout:
×
1854
                return lnrpc.PaymentFailureReason_FAILURE_REASON_TIMEOUT, nil
×
1855

UNCOV
1856
        case paymentsdb.FailureReasonNoRoute:
×
UNCOV
1857
                return lnrpc.PaymentFailureReason_FAILURE_REASON_NO_ROUTE, nil
×
1858

UNCOV
1859
        case paymentsdb.FailureReasonError:
×
UNCOV
1860
                return lnrpc.PaymentFailureReason_FAILURE_REASON_ERROR, nil
×
1861

UNCOV
1862
        case paymentsdb.FailureReasonPaymentDetails:
×
UNCOV
1863
                return lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS, nil
×
1864

UNCOV
1865
        case paymentsdb.FailureReasonInsufficientBalance:
×
UNCOV
1866
                return lnrpc.PaymentFailureReason_FAILURE_REASON_INSUFFICIENT_BALANCE, nil
×
1867

UNCOV
1868
        case paymentsdb.FailureReasonCanceled:
×
UNCOV
1869
                return lnrpc.PaymentFailureReason_FAILURE_REASON_CANCELED, nil
×
1870
        }
1871

1872
        return 0, errors.New("unknown failure reason")
×
1873
}
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