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

lightningnetwork / lnd / 10190851021

01 Aug 2024 02:21AM UTC coverage: 58.627% (-0.01%) from 58.641%
10190851021

push

github

web-flow
Merge pull request #8764 from ellemouton/rb-send-via-multi-path

[3/4] Route Blinding: send MPP over multiple blinded paths

197 of 248 new or added lines in 7 files covered. (79.44%)

249 existing lines in 19 files now uncovered.

125259 of 213655 relevant lines covered (58.63%)

28116.45 hits per line

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

67.89
/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/channeldb"
18
        "github.com/lightningnetwork/lnd/feature"
19
        "github.com/lightningnetwork/lnd/htlcswitch"
20
        "github.com/lightningnetwork/lnd/lnrpc"
21
        "github.com/lightningnetwork/lnd/lntypes"
22
        "github.com/lightningnetwork/lnd/lnwire"
23
        "github.com/lightningnetwork/lnd/record"
24
        "github.com/lightningnetwork/lnd/routing"
25
        "github.com/lightningnetwork/lnd/routing/route"
26
        "github.com/lightningnetwork/lnd/subscribe"
27
        "github.com/lightningnetwork/lnd/zpay32"
28
)
29

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

39
// RouterBackend contains the backend implementation of the router rpc sub
40
// server calls.
41
type RouterBackend struct {
42
        // SelfNode is the vertex of the node sending the payment.
43
        SelfNode route.Vertex
44

45
        // FetchChannelCapacity is a closure that we'll use the fetch the total
46
        // capacity of a channel to populate in responses.
47
        FetchChannelCapacity func(chanID uint64) (btcutil.Amount, error)
48

49
        // FetchAmountPairCapacity determines the maximal channel capacity
50
        // between two nodes given a certain amount.
51
        FetchAmountPairCapacity func(nodeFrom, nodeTo route.Vertex,
52
                amount lnwire.MilliSatoshi) (btcutil.Amount, error)
53

54
        // FetchChannelEndpoints returns the pubkeys of both endpoints of the
55
        // given channel id.
56
        FetchChannelEndpoints func(chanID uint64) (route.Vertex,
57
                route.Vertex, error)
58

59
        // FindRoute is a closure that abstracts away how we locate/query for
60
        // routes.
61
        FindRoute func(*routing.RouteRequest) (*route.Route, float64, error)
62

63
        MissionControl MissionControl
64

65
        // ActiveNetParams are the network parameters of the primary network
66
        // that the route is operating on. This is necessary so we can ensure
67
        // that we receive payment requests that send to destinations on our
68
        // network.
69
        ActiveNetParams *chaincfg.Params
70

71
        // Tower is the ControlTower instance that is used to track pending
72
        // payments.
73
        Tower routing.ControlTower
74

75
        // MaxTotalTimelock is the maximum total time lock a route is allowed to
76
        // have.
77
        MaxTotalTimelock uint32
78

79
        // DefaultFinalCltvDelta is the default value used as final cltv delta
80
        // when an RPC caller doesn't specify a value.
81
        DefaultFinalCltvDelta uint16
82

83
        // SubscribeHtlcEvents returns a subscription client for the node's
84
        // htlc events.
85
        SubscribeHtlcEvents func() (*subscribe.Client, error)
86

87
        // InterceptableForwarder exposes the ability to intercept forward events
88
        // by letting the router register a ForwardInterceptor.
89
        InterceptableForwarder htlcswitch.InterceptableHtlcForwarder
90

91
        // SetChannelEnabled exposes the ability to manually enable a channel.
92
        SetChannelEnabled func(wire.OutPoint) error
93

94
        // SetChannelDisabled exposes the ability to manually disable a channel
95
        SetChannelDisabled func(wire.OutPoint) error
96

97
        // SetChannelAuto exposes the ability to restore automatic channel state
98
        // management after manually setting channel status.
99
        SetChannelAuto func(wire.OutPoint) error
100

101
        // UseStatusInitiated is a boolean that indicates whether the router
102
        // should use the new status code `Payment_INITIATED`.
103
        //
104
        // TODO(yy): remove this config after the new status code is fully
105
        // deployed to the network(v0.20.0).
106
        UseStatusInitiated bool
107
}
108

109
// MissionControl defines the mission control dependencies of routerrpc.
110
type MissionControl interface {
111
        // GetProbability is expected to return the success probability of a
112
        // payment from fromNode to toNode.
113
        GetProbability(fromNode, toNode route.Vertex,
114
                amt lnwire.MilliSatoshi, capacity btcutil.Amount) float64
115

116
        // ResetHistory resets the history of MissionControl returning it to a
117
        // state as if no payment attempts have been made.
118
        ResetHistory() error
119

120
        // GetHistorySnapshot takes a snapshot from the current mission control
121
        // state and actual probability estimates.
122
        GetHistorySnapshot() *routing.MissionControlSnapshot
123

124
        // ImportHistory imports the mission control snapshot to our internal
125
        // state. This import will only be applied in-memory, and will not be
126
        // persisted across restarts.
127
        ImportHistory(snapshot *routing.MissionControlSnapshot, force bool) error
128

129
        // GetPairHistorySnapshot returns the stored history for a given node
130
        // pair.
131
        GetPairHistorySnapshot(fromNode,
132
                toNode route.Vertex) routing.TimedPairResult
133

134
        // GetConfig gets mission control's current config.
135
        GetConfig() *routing.MissionControlConfig
136

137
        // SetConfig sets mission control's config to the values provided, if
138
        // they are valid.
139
        SetConfig(cfg *routing.MissionControlConfig) error
140
}
141

142
// QueryRoutes attempts to query the daemons' Channel Router for a possible
143
// route to a target destination capable of carrying a specific amount of
144
// satoshis within the route's flow. The returned route contains the full
145
// details required to craft and send an HTLC, also including the necessary
146
// information that should be present within the Sphinx packet encapsulated
147
// within the HTLC.
148
//
149
// TODO(roasbeef): should return a slice of routes in reality * create separate
150
// PR to send based on well formatted route
151
func (r *RouterBackend) QueryRoutes(ctx context.Context,
152
        in *lnrpc.QueryRoutesRequest) (*lnrpc.QueryRoutesResponse, error) {
3✔
153

3✔
154
        routeReq, err := r.parseQueryRoutesRequest(in)
3✔
155
        if err != nil {
3✔
156
                return nil, err
×
157
        }
×
158

159
        // Query the channel router for a possible path to the destination that
160
        // can carry `in.Amt` satoshis _including_ the total fee required on
161
        // the route
162
        route, successProb, err := r.FindRoute(routeReq)
3✔
163
        if err != nil {
3✔
164
                return nil, err
×
165
        }
×
166

167
        // For each valid route, we'll convert the result into the format
168
        // required by the RPC system.
169
        rpcRoute, err := r.MarshallRoute(route)
3✔
170
        if err != nil {
3✔
171
                return nil, err
×
172
        }
×
173

174
        routeResp := &lnrpc.QueryRoutesResponse{
3✔
175
                Routes:      []*lnrpc.Route{rpcRoute},
3✔
176
                SuccessProb: successProb,
3✔
177
        }
3✔
178

3✔
179
        return routeResp, nil
3✔
180
}
181

182
func parsePubKey(key string) (route.Vertex, error) {
3✔
183
        pubKeyBytes, err := hex.DecodeString(key)
3✔
184
        if err != nil {
3✔
185
                return route.Vertex{}, err
×
186
        }
×
187

188
        return route.NewVertexFromBytes(pubKeyBytes)
3✔
189
}
190

191
func (r *RouterBackend) parseIgnored(in *lnrpc.QueryRoutesRequest) (
192
        map[route.Vertex]struct{}, map[routing.DirectedNodePair]struct{},
193
        error) {
3✔
194

3✔
195
        ignoredNodes := make(map[route.Vertex]struct{})
3✔
196
        for _, ignorePubKey := range in.IgnoredNodes {
3✔
197
                ignoreVertex, err := route.NewVertexFromBytes(ignorePubKey)
×
198
                if err != nil {
×
199
                        return nil, nil, err
×
200
                }
×
201
                ignoredNodes[ignoreVertex] = struct{}{}
×
202
        }
203

204
        ignoredPairs := make(map[routing.DirectedNodePair]struct{})
3✔
205

3✔
206
        // Convert deprecated ignoredEdges to pairs.
3✔
207
        for _, ignoredEdge := range in.IgnoredEdges {
3✔
208
                pair, err := r.rpcEdgeToPair(ignoredEdge)
×
209
                if err != nil {
×
210
                        log.Warnf("Ignore channel %v skipped: %v",
×
211
                                ignoredEdge.ChannelId, err)
×
212

×
213
                        continue
×
214
                }
215
                ignoredPairs[pair] = struct{}{}
×
216
        }
217

218
        // Add ignored pairs to set.
219
        for _, ignorePair := range in.IgnoredPairs {
3✔
220
                from, err := route.NewVertexFromBytes(ignorePair.From)
×
221
                if err != nil {
×
222
                        return nil, nil, err
×
223
                }
×
224

225
                to, err := route.NewVertexFromBytes(ignorePair.To)
×
226
                if err != nil {
×
227
                        return nil, nil, err
×
228
                }
×
229

230
                pair := routing.NewDirectedNodePair(from, to)
×
231
                ignoredPairs[pair] = struct{}{}
×
232
        }
233

234
        return ignoredNodes, ignoredPairs, nil
3✔
235
}
236

237
func (r *RouterBackend) parseQueryRoutesRequest(in *lnrpc.QueryRoutesRequest) (
238
        *routing.RouteRequest, error) {
3✔
239

3✔
240
        // Parse the hex-encoded source public key into a full public key that
3✔
241
        // we can properly manipulate.
3✔
242

3✔
243
        var sourcePubKey route.Vertex
3✔
244
        if in.SourcePubKey != "" {
3✔
245
                var err error
×
246
                sourcePubKey, err = parsePubKey(in.SourcePubKey)
×
247
                if err != nil {
×
248
                        return nil, err
×
249
                }
×
250
        } else {
3✔
251
                // If no source is specified, use self.
3✔
252
                sourcePubKey = r.SelfNode
3✔
253
        }
3✔
254

255
        // Currently, within the bootstrap phase of the network, we limit the
256
        // largest payment size allotted to (2^32) - 1 mSAT or 4.29 million
257
        // satoshis.
258
        amt, err := lnrpc.UnmarshallAmt(in.Amt, in.AmtMsat)
3✔
259
        if err != nil {
3✔
260
                return nil, err
×
261
        }
×
262

263
        // Unmarshall restrictions from request.
264
        feeLimit := lnrpc.CalculateFeeLimit(in.FeeLimit, amt)
3✔
265

3✔
266
        // Since QueryRoutes allows having a different source other than
3✔
267
        // ourselves, we'll only apply our max time lock if we are the source.
3✔
268
        maxTotalTimelock := r.MaxTotalTimelock
3✔
269
        if sourcePubKey != r.SelfNode {
3✔
270
                maxTotalTimelock = math.MaxUint32
×
271
        }
×
272

273
        cltvLimit, err := ValidateCLTVLimit(in.CltvLimit, maxTotalTimelock)
3✔
274
        if err != nil {
3✔
275
                return nil, err
×
276
        }
×
277

278
        // If we have a blinded path set, we'll get a few of our fields from
279
        // inside of the path rather than the request's fields.
280
        var (
3✔
281
                targetPubKey   *route.Vertex
3✔
282
                routeHintEdges map[route.Vertex][]routing.AdditionalEdge
3✔
283
                blindedPathSet *routing.BlindedPaymentPathSet
3✔
284

3✔
285
                // finalCLTVDelta varies depending on whether we're sending to
3✔
286
                // a blinded route or an unblinded node. For blinded paths,
3✔
287
                // our final cltv is already baked into the path so we restrict
3✔
288
                // this value to zero on the API. Bolt11 invoices have a
3✔
289
                // default, so we'll fill that in for the non-blinded case.
3✔
290
                finalCLTVDelta uint16
3✔
291

3✔
292
                // destinationFeatures is the set of features for the
3✔
293
                // destination node.
3✔
294
                destinationFeatures *lnwire.FeatureVector
3✔
295
        )
3✔
296

3✔
297
        // Validate that the fields provided in the request are sane depending
3✔
298
        // on whether it is using a blinded path or not.
3✔
299
        if len(in.BlindedPaymentPaths) > 0 {
6✔
300
                blindedPathSet, err = parseBlindedPaymentPaths(in)
3✔
301
                if err != nil {
3✔
302
                        return nil, err
×
303
                }
×
304

305
                pathFeatures := blindedPathSet.Features()
3✔
306
                if pathFeatures != nil {
3✔
NEW
307
                        destinationFeatures = pathFeatures.Clone()
×
UNCOV
308
                }
×
309
        } else {
3✔
310
                // If we do not have a blinded path, a target pubkey must be
3✔
311
                // set.
3✔
312
                pk, err := parsePubKey(in.PubKey)
3✔
313
                if err != nil {
3✔
314
                        return nil, err
×
315
                }
×
316
                targetPubKey = &pk
3✔
317

3✔
318
                // Convert route hints to an edge map.
3✔
319
                routeHints, err := unmarshallRouteHints(in.RouteHints)
3✔
320
                if err != nil {
3✔
321
                        return nil, err
×
322
                }
×
323

324
                routeHintEdges, err = routing.RouteHintsToEdges(
3✔
325
                        routeHints, *targetPubKey,
3✔
326
                )
3✔
327
                if err != nil {
3✔
328
                        return nil, err
×
329
                }
×
330

331
                // Set a non-zero final CLTV delta for payments that are not
332
                // to blinded paths, as bolt11 has a default final cltv delta
333
                // value that is used in the absence of a value.
334
                finalCLTVDelta = r.DefaultFinalCltvDelta
3✔
335
                if in.FinalCltvDelta != 0 {
6✔
336
                        finalCLTVDelta = uint16(in.FinalCltvDelta)
3✔
337
                }
3✔
338

339
                // Do bounds checking without block padding so we don't give
340
                // routes that will leave the router in a zombie payment state.
341
                err = routing.ValidateCLTVLimit(
3✔
342
                        cltvLimit, finalCLTVDelta, false,
3✔
343
                )
3✔
344
                if err != nil {
3✔
345
                        return nil, err
×
346
                }
×
347

348
                // Parse destination feature bits.
349
                destinationFeatures, err = UnmarshalFeatures(in.DestFeatures)
3✔
350
                if err != nil {
3✔
351
                        return nil, err
×
352
                }
×
353
        }
354

355
        // We need to subtract the final delta before passing it into path
356
        // finding. The optimal path is independent of the final cltv delta and
357
        // the path finding algorithm is unaware of this value.
358
        cltvLimit -= uint32(finalCLTVDelta)
3✔
359

3✔
360
        ignoredNodes, ignoredPairs, err := r.parseIgnored(in)
3✔
361
        if err != nil {
3✔
362
                return nil, err
×
363
        }
×
364

365
        restrictions := &routing.RestrictParams{
3✔
366
                FeeLimit: feeLimit,
3✔
367
                ProbabilitySource: func(fromNode, toNode route.Vertex,
3✔
368
                        amt lnwire.MilliSatoshi,
3✔
369
                        capacity btcutil.Amount) float64 {
6✔
370

3✔
371
                        if _, ok := ignoredNodes[fromNode]; ok {
3✔
372
                                return 0
×
373
                        }
×
374

375
                        pair := routing.DirectedNodePair{
3✔
376
                                From: fromNode,
3✔
377
                                To:   toNode,
3✔
378
                        }
3✔
379
                        if _, ok := ignoredPairs[pair]; ok {
3✔
380
                                return 0
×
381
                        }
×
382

383
                        if !in.UseMissionControl {
6✔
384
                                return 1
3✔
385
                        }
3✔
386

387
                        return r.MissionControl.GetProbability(
×
388
                                fromNode, toNode, amt, capacity,
×
389
                        )
×
390
                },
391
                DestCustomRecords:     record.CustomSet(in.DestCustomRecords),
392
                CltvLimit:             cltvLimit,
393
                DestFeatures:          destinationFeatures,
394
                BlindedPaymentPathSet: blindedPathSet,
395
        }
396

397
        // Pass along an outgoing channel restriction if specified.
398
        if in.OutgoingChanId != 0 {
3✔
399
                restrictions.OutgoingChannelIDs = []uint64{in.OutgoingChanId}
×
400
        }
×
401

402
        // Pass along a last hop restriction if specified.
403
        if len(in.LastHopPubkey) > 0 {
3✔
404
                lastHop, err := route.NewVertexFromBytes(
×
405
                        in.LastHopPubkey,
×
406
                )
×
407
                if err != nil {
×
408
                        return nil, err
×
409
                }
×
410
                restrictions.LastHop = &lastHop
×
411
        }
412

413
        // If we have any TLV records destined for the final hop, then we'll
414
        // attempt to decode them now into a form that the router can more
415
        // easily manipulate.
416
        customRecords := record.CustomSet(in.DestCustomRecords)
3✔
417
        if err := customRecords.Validate(); err != nil {
3✔
418
                return nil, err
×
419
        }
×
420

421
        return routing.NewRouteRequest(
3✔
422
                sourcePubKey, targetPubKey, amt, in.TimePref, restrictions,
3✔
423
                customRecords, routeHintEdges, blindedPathSet,
3✔
424
                finalCLTVDelta,
3✔
425
        )
3✔
426
}
427

428
func parseBlindedPaymentPaths(in *lnrpc.QueryRoutesRequest) (
429
        *routing.BlindedPaymentPathSet, error) {
3✔
430

3✔
431
        if len(in.PubKey) != 0 {
3✔
432
                return nil, fmt.Errorf("target pubkey: %x should not be set "+
×
433
                        "when blinded path is provided", in.PubKey)
×
434
        }
×
435

436
        if len(in.RouteHints) > 0 {
3✔
437
                return nil, errors.New("route hints and blinded path can't " +
×
438
                        "both be set")
×
439
        }
×
440

441
        if in.FinalCltvDelta != 0 {
3✔
UNCOV
442
                return nil, errors.New("final cltv delta should be " +
×
443
                        "zero for blinded paths")
×
444
        }
×
445

446
        // For blinded paths, we get one set of features for the relaying
447
        // intermediate nodes and the final destination. We don't allow the
448
        // destination feature bit field for regular payments to be set, as
449
        // this could lead to ambiguity.
450
        if len(in.DestFeatures) > 0 {
3✔
451
                return nil, errors.New("destination features should " +
×
452
                        "be populated in blinded path")
×
453
        }
×
454

455
        paths := make([]*routing.BlindedPayment, len(in.BlindedPaymentPaths))
3✔
456
        for i, paymentPath := range in.BlindedPaymentPaths {
6✔
457
                blindedPmt, err := unmarshalBlindedPayment(paymentPath)
3✔
458
                if err != nil {
3✔
NEW
459
                        return nil, fmt.Errorf("parse blinded payment: %w", err)
×
NEW
460
                }
×
461

462
                if err := blindedPmt.Validate(); err != nil {
3✔
NEW
463
                        return nil, fmt.Errorf("invalid blinded path: %w", err)
×
NEW
464
                }
×
465

466
                paths[i] = blindedPmt
3✔
467
        }
468

469
        return routing.NewBlindedPaymentPathSet(paths)
3✔
470
}
471

472
func unmarshalBlindedPayment(rpcPayment *lnrpc.BlindedPaymentPath) (
473
        *routing.BlindedPayment, error) {
3✔
474

3✔
475
        if rpcPayment == nil {
3✔
476
                return nil, errors.New("nil blinded payment")
×
477
        }
×
478

479
        path, err := unmarshalBlindedPaymentPaths(rpcPayment.BlindedPath)
3✔
480
        if err != nil {
3✔
481
                return nil, err
×
482
        }
×
483

484
        features, err := UnmarshalFeatures(rpcPayment.Features)
3✔
485
        if err != nil {
3✔
486
                return nil, err
×
487
        }
×
488

489
        return &routing.BlindedPayment{
3✔
490
                BlindedPath:         path,
3✔
491
                CltvExpiryDelta:     uint16(rpcPayment.TotalCltvDelta),
3✔
492
                BaseFee:             uint32(rpcPayment.BaseFeeMsat),
3✔
493
                ProportionalFeeRate: rpcPayment.ProportionalFeeRate,
3✔
494
                HtlcMinimum:         rpcPayment.HtlcMinMsat,
3✔
495
                HtlcMaximum:         rpcPayment.HtlcMaxMsat,
3✔
496
                Features:            features,
3✔
497
        }, nil
3✔
498
}
499

500
func unmarshalBlindedPaymentPaths(rpcPath *lnrpc.BlindedPath) (
501
        *sphinx.BlindedPath, error) {
3✔
502

3✔
503
        if rpcPath == nil {
3✔
504
                return nil, errors.New("blinded path required when blinded " +
×
505
                        "route is provided")
×
506
        }
×
507

508
        introduction, err := btcec.ParsePubKey(rpcPath.IntroductionNode)
3✔
509
        if err != nil {
3✔
510
                return nil, err
×
511
        }
×
512

513
        blinding, err := btcec.ParsePubKey(rpcPath.BlindingPoint)
3✔
514
        if err != nil {
3✔
515
                return nil, err
×
516
        }
×
517

518
        if len(rpcPath.BlindedHops) < 1 {
3✔
519
                return nil, errors.New("at least 1 blinded hops required")
×
520
        }
×
521

522
        path := &sphinx.BlindedPath{
3✔
523
                IntroductionPoint: introduction,
3✔
524
                BlindingPoint:     blinding,
3✔
525
                BlindedHops: make(
3✔
526
                        []*sphinx.BlindedHopInfo, len(rpcPath.BlindedHops),
3✔
527
                ),
3✔
528
        }
3✔
529

3✔
530
        for i, hop := range rpcPath.BlindedHops {
6✔
531
                path.BlindedHops[i], err = unmarshalBlindedHop(hop)
3✔
532
                if err != nil {
3✔
533
                        return nil, err
×
534
                }
×
535
        }
536

537
        return path, nil
3✔
538
}
539

540
func unmarshalBlindedHop(rpcHop *lnrpc.BlindedHop) (*sphinx.BlindedHopInfo,
541
        error) {
3✔
542

3✔
543
        pubkey, err := btcec.ParsePubKey(rpcHop.BlindedNode)
3✔
544
        if err != nil {
3✔
545
                return nil, err
×
546
        }
×
547

548
        if len(rpcHop.EncryptedData) == 0 {
3✔
549
                return nil, errors.New("empty encrypted data not allowed")
×
550
        }
×
551

552
        return &sphinx.BlindedHopInfo{
3✔
553
                BlindedNodePub: pubkey,
3✔
554
                CipherText:     rpcHop.EncryptedData,
3✔
555
        }, nil
3✔
556
}
557

558
// rpcEdgeToPair looks up the provided channel and returns the channel endpoints
559
// as a directed pair.
560
func (r *RouterBackend) rpcEdgeToPair(e *lnrpc.EdgeLocator) (
561
        routing.DirectedNodePair, error) {
×
562

×
563
        a, b, err := r.FetchChannelEndpoints(e.ChannelId)
×
564
        if err != nil {
×
565
                return routing.DirectedNodePair{}, err
×
566
        }
×
567

568
        var pair routing.DirectedNodePair
×
569
        if e.DirectionReverse {
×
570
                pair.From, pair.To = b, a
×
571
        } else {
×
572
                pair.From, pair.To = a, b
×
573
        }
×
574

575
        return pair, nil
×
576
}
577

578
// MarshallRoute marshalls an internal route to an rpc route struct.
579
func (r *RouterBackend) MarshallRoute(route *route.Route) (*lnrpc.Route, error) {
3✔
580
        resp := &lnrpc.Route{
3✔
581
                TotalTimeLock: route.TotalTimeLock,
3✔
582
                TotalFees:     int64(route.TotalFees().ToSatoshis()),
3✔
583
                TotalFeesMsat: int64(route.TotalFees()),
3✔
584
                TotalAmt:      int64(route.TotalAmount.ToSatoshis()),
3✔
585
                TotalAmtMsat:  int64(route.TotalAmount),
3✔
586
                Hops:          make([]*lnrpc.Hop, len(route.Hops)),
3✔
587
        }
3✔
588
        incomingAmt := route.TotalAmount
3✔
589
        for i, hop := range route.Hops {
6✔
590
                fee := route.HopFee(i)
3✔
591

3✔
592
                // Channel capacity is not a defining property of a route. For
3✔
593
                // backwards RPC compatibility, we retrieve it here from the
3✔
594
                // graph.
3✔
595
                chanCapacity, err := r.FetchChannelCapacity(hop.ChannelID)
3✔
596
                if err != nil {
6✔
597
                        // If capacity cannot be retrieved, this may be a
3✔
598
                        // not-yet-received or private channel. Then report
3✔
599
                        // amount that is sent through the channel as capacity.
3✔
600
                        chanCapacity = incomingAmt.ToSatoshis()
3✔
601
                }
3✔
602

603
                // Extract the MPP fields if present on this hop.
604
                var mpp *lnrpc.MPPRecord
3✔
605
                if hop.MPP != nil {
6✔
606
                        addr := hop.MPP.PaymentAddr()
3✔
607

3✔
608
                        mpp = &lnrpc.MPPRecord{
3✔
609
                                PaymentAddr:  addr[:],
3✔
610
                                TotalAmtMsat: int64(hop.MPP.TotalMsat()),
3✔
611
                        }
3✔
612
                }
3✔
613

614
                var amp *lnrpc.AMPRecord
3✔
615
                if hop.AMP != nil {
6✔
616
                        rootShare := hop.AMP.RootShare()
3✔
617
                        setID := hop.AMP.SetID()
3✔
618

3✔
619
                        amp = &lnrpc.AMPRecord{
3✔
620
                                RootShare:  rootShare[:],
3✔
621
                                SetId:      setID[:],
3✔
622
                                ChildIndex: hop.AMP.ChildIndex(),
3✔
623
                        }
3✔
624
                }
3✔
625

626
                resp.Hops[i] = &lnrpc.Hop{
3✔
627
                        ChanId:           hop.ChannelID,
3✔
628
                        ChanCapacity:     int64(chanCapacity),
3✔
629
                        AmtToForward:     int64(hop.AmtToForward.ToSatoshis()),
3✔
630
                        AmtToForwardMsat: int64(hop.AmtToForward),
3✔
631
                        Fee:              int64(fee.ToSatoshis()),
3✔
632
                        FeeMsat:          int64(fee),
3✔
633
                        Expiry:           uint32(hop.OutgoingTimeLock),
3✔
634
                        PubKey: hex.EncodeToString(
3✔
635
                                hop.PubKeyBytes[:],
3✔
636
                        ),
3✔
637
                        CustomRecords: hop.CustomRecords,
3✔
638
                        TlvPayload:    !hop.LegacyPayload,
3✔
639
                        MppRecord:     mpp,
3✔
640
                        AmpRecord:     amp,
3✔
641
                        Metadata:      hop.Metadata,
3✔
642
                        EncryptedData: hop.EncryptedData,
3✔
643
                        TotalAmtMsat:  uint64(hop.TotalAmtMsat),
3✔
644
                }
3✔
645

3✔
646
                if hop.BlindingPoint != nil {
6✔
647
                        blinding := hop.BlindingPoint.SerializeCompressed()
3✔
648
                        resp.Hops[i].BlindingPoint = blinding
3✔
649
                }
3✔
650
                incomingAmt = hop.AmtToForward
3✔
651
        }
652

653
        return resp, nil
3✔
654
}
655

656
// UnmarshallHopWithPubkey unmarshalls an rpc hop for which the pubkey has
657
// already been extracted.
658
func UnmarshallHopWithPubkey(rpcHop *lnrpc.Hop, pubkey route.Vertex) (*route.Hop,
659
        error) {
3✔
660

3✔
661
        customRecords := record.CustomSet(rpcHop.CustomRecords)
3✔
662
        if err := customRecords.Validate(); err != nil {
3✔
663
                return nil, err
×
664
        }
×
665

666
        mpp, err := UnmarshalMPP(rpcHop.MppRecord)
3✔
667
        if err != nil {
3✔
668
                return nil, err
×
669
        }
×
670

671
        amp, err := UnmarshalAMP(rpcHop.AmpRecord)
3✔
672
        if err != nil {
3✔
673
                return nil, err
×
674
        }
×
675

676
        hop := &route.Hop{
3✔
677
                OutgoingTimeLock: rpcHop.Expiry,
3✔
678
                AmtToForward:     lnwire.MilliSatoshi(rpcHop.AmtToForwardMsat),
3✔
679
                PubKeyBytes:      pubkey,
3✔
680
                ChannelID:        rpcHop.ChanId,
3✔
681
                CustomRecords:    customRecords,
3✔
682
                LegacyPayload:    false,
3✔
683
                MPP:              mpp,
3✔
684
                AMP:              amp,
3✔
685
                EncryptedData:    rpcHop.EncryptedData,
3✔
686
                TotalAmtMsat:     lnwire.MilliSatoshi(rpcHop.TotalAmtMsat),
3✔
687
        }
3✔
688

3✔
689
        haveBlindingPoint := len(rpcHop.BlindingPoint) != 0
3✔
690
        if haveBlindingPoint {
6✔
691
                hop.BlindingPoint, err = btcec.ParsePubKey(
3✔
692
                        rpcHop.BlindingPoint,
3✔
693
                )
3✔
694
                if err != nil {
3✔
695
                        return nil, fmt.Errorf("blinding point: %w", err)
×
696
                }
×
697
        }
698

699
        if haveBlindingPoint && len(rpcHop.EncryptedData) == 0 {
3✔
700
                return nil, errors.New("encrypted data should be present if " +
×
701
                        "blinding point is provided")
×
702
        }
×
703

704
        return hop, nil
3✔
705
}
706

707
// UnmarshallHop unmarshalls an rpc hop that may or may not contain a node
708
// pubkey.
709
func (r *RouterBackend) UnmarshallHop(rpcHop *lnrpc.Hop,
710
        prevNodePubKey [33]byte) (*route.Hop, error) {
3✔
711

3✔
712
        var pubKeyBytes [33]byte
3✔
713
        if rpcHop.PubKey != "" {
6✔
714
                // Unmarshall the provided hop pubkey.
3✔
715
                pubKey, err := hex.DecodeString(rpcHop.PubKey)
3✔
716
                if err != nil {
3✔
717
                        return nil, fmt.Errorf("cannot decode pubkey %s",
×
718
                                rpcHop.PubKey)
×
719
                }
×
720
                copy(pubKeyBytes[:], pubKey)
3✔
721
        } else {
×
722
                // If no pub key is given of the hop, the local channel graph
×
723
                // needs to be queried to complete the information necessary for
×
724
                // routing. Discard edge policies, because they may be nil.
×
725
                node1, node2, err := r.FetchChannelEndpoints(rpcHop.ChanId)
×
726
                if err != nil {
×
727
                        return nil, err
×
728
                }
×
729

730
                switch {
×
731
                case prevNodePubKey == node1:
×
732
                        pubKeyBytes = node2
×
733
                case prevNodePubKey == node2:
×
734
                        pubKeyBytes = node1
×
735
                default:
×
736
                        return nil, fmt.Errorf("channel edge does not match " +
×
737
                                "expected node")
×
738
                }
739
        }
740

741
        return UnmarshallHopWithPubkey(rpcHop, pubKeyBytes)
3✔
742
}
743

744
// UnmarshallRoute unmarshalls an rpc route. For hops that don't specify a
745
// pubkey, the channel graph is queried.
746
func (r *RouterBackend) UnmarshallRoute(rpcroute *lnrpc.Route) (
747
        *route.Route, error) {
3✔
748

3✔
749
        prevNodePubKey := r.SelfNode
3✔
750

3✔
751
        hops := make([]*route.Hop, len(rpcroute.Hops))
3✔
752
        for i, hop := range rpcroute.Hops {
6✔
753
                routeHop, err := r.UnmarshallHop(hop, prevNodePubKey)
3✔
754
                if err != nil {
3✔
755
                        return nil, err
×
756
                }
×
757

758
                hops[i] = routeHop
3✔
759

3✔
760
                prevNodePubKey = routeHop.PubKeyBytes
3✔
761
        }
762

763
        route, err := route.NewRouteFromHops(
3✔
764
                lnwire.MilliSatoshi(rpcroute.TotalAmtMsat),
3✔
765
                rpcroute.TotalTimeLock,
3✔
766
                r.SelfNode,
3✔
767
                hops,
3✔
768
        )
3✔
769
        if err != nil {
3✔
770
                return nil, err
×
771
        }
×
772

773
        return route, nil
3✔
774
}
775

776
// extractIntentFromSendRequest attempts to parse the SendRequest details
777
// required to dispatch a client from the information presented by an RPC
778
// client.
779
func (r *RouterBackend) extractIntentFromSendRequest(
780
        rpcPayReq *SendPaymentRequest) (*routing.LightningPayment, error) {
3✔
781

3✔
782
        payIntent := &routing.LightningPayment{}
3✔
783

3✔
784
        // Pass along time preference.
3✔
785
        if rpcPayReq.TimePref < -1 || rpcPayReq.TimePref > 1 {
3✔
786
                return nil, errors.New("time preference out of range")
×
787
        }
×
788
        payIntent.TimePref = rpcPayReq.TimePref
3✔
789

3✔
790
        // Pass along restrictions on the outgoing channels that may be used.
3✔
791
        payIntent.OutgoingChannelIDs = rpcPayReq.OutgoingChanIds
3✔
792

3✔
793
        // Add the deprecated single outgoing channel restriction if present.
3✔
794
        if rpcPayReq.OutgoingChanId != 0 {
3✔
795
                if payIntent.OutgoingChannelIDs != nil {
×
796
                        return nil, errors.New("outgoing_chan_id and " +
×
797
                                "outgoing_chan_ids are mutually exclusive")
×
798
                }
×
799

800
                payIntent.OutgoingChannelIDs = append(
×
801
                        payIntent.OutgoingChannelIDs, rpcPayReq.OutgoingChanId,
×
802
                )
×
803
        }
804

805
        // Pass along a last hop restriction if specified.
806
        if len(rpcPayReq.LastHopPubkey) > 0 {
3✔
807
                lastHop, err := route.NewVertexFromBytes(
×
808
                        rpcPayReq.LastHopPubkey,
×
809
                )
×
810
                if err != nil {
×
811
                        return nil, err
×
812
                }
×
813
                payIntent.LastHop = &lastHop
×
814
        }
815

816
        // Take the CLTV limit from the request if set, otherwise use the max.
817
        cltvLimit, err := ValidateCLTVLimit(
3✔
818
                uint32(rpcPayReq.CltvLimit), r.MaxTotalTimelock,
3✔
819
        )
3✔
820
        if err != nil {
3✔
821
                return nil, err
×
822
        }
×
823
        payIntent.CltvLimit = cltvLimit
3✔
824

3✔
825
        // Attempt to parse the max parts value set by the user, if this value
3✔
826
        // isn't set, then we'll use the current default value for this
3✔
827
        // setting.
3✔
828
        maxParts := rpcPayReq.MaxParts
3✔
829
        if maxParts == 0 {
6✔
830
                maxParts = DefaultMaxParts
3✔
831
        }
3✔
832
        payIntent.MaxParts = maxParts
3✔
833

3✔
834
        // If this payment had a max shard amount specified, then we'll apply
3✔
835
        // that now, which'll force us to always make payment splits smaller
3✔
836
        // than this.
3✔
837
        if rpcPayReq.MaxShardSizeMsat > 0 {
3✔
838
                shardAmtMsat := lnwire.MilliSatoshi(rpcPayReq.MaxShardSizeMsat)
×
839
                payIntent.MaxShardAmt = &shardAmtMsat
×
840
        }
×
841

842
        // Take fee limit from request.
843
        payIntent.FeeLimit, err = lnrpc.UnmarshallAmt(
3✔
844
                rpcPayReq.FeeLimitSat, rpcPayReq.FeeLimitMsat,
3✔
845
        )
3✔
846
        if err != nil {
3✔
847
                return nil, err
×
848
        }
×
849

850
        // Set payment attempt timeout.
851
        if rpcPayReq.TimeoutSeconds == 0 {
3✔
852
                return nil, errors.New("timeout_seconds must be specified")
×
853
        }
×
854

855
        customRecords := record.CustomSet(rpcPayReq.DestCustomRecords)
3✔
856
        if err := customRecords.Validate(); err != nil {
3✔
857
                return nil, err
×
858
        }
×
859
        payIntent.DestCustomRecords = customRecords
3✔
860

3✔
861
        payIntent.PayAttemptTimeout = time.Second *
3✔
862
                time.Duration(rpcPayReq.TimeoutSeconds)
3✔
863

3✔
864
        // Route hints.
3✔
865
        routeHints, err := unmarshallRouteHints(
3✔
866
                rpcPayReq.RouteHints,
3✔
867
        )
3✔
868
        if err != nil {
3✔
869
                return nil, err
×
870
        }
×
871
        payIntent.RouteHints = routeHints
3✔
872

3✔
873
        // Unmarshall either sat or msat amount from request.
3✔
874
        reqAmt, err := lnrpc.UnmarshallAmt(
3✔
875
                rpcPayReq.Amt, rpcPayReq.AmtMsat,
3✔
876
        )
3✔
877
        if err != nil {
3✔
878
                return nil, err
×
879
        }
×
880

881
        // If the payment request field isn't blank, then the details of the
882
        // invoice are encoded entirely within the encoded payReq.  So we'll
883
        // attempt to decode it, populating the payment accordingly.
884
        if rpcPayReq.PaymentRequest != "" {
6✔
885
                switch {
3✔
886

887
                case len(rpcPayReq.Dest) > 0:
×
888
                        return nil, errors.New("dest and payment_request " +
×
889
                                "cannot appear together")
×
890

891
                case len(rpcPayReq.PaymentHash) > 0:
×
892
                        return nil, errors.New("payment_hash and payment_request " +
×
893
                                "cannot appear together")
×
894

895
                case rpcPayReq.FinalCltvDelta != 0:
×
896
                        return nil, errors.New("final_cltv_delta and payment_request " +
×
897
                                "cannot appear together")
×
898
                }
899

900
                payReq, err := zpay32.Decode(
3✔
901
                        rpcPayReq.PaymentRequest, r.ActiveNetParams,
3✔
902
                )
3✔
903
                if err != nil {
3✔
904
                        return nil, err
×
905
                }
×
906

907
                // Next, we'll ensure that this payreq hasn't already expired.
908
                err = ValidatePayReqExpiry(payReq)
3✔
909
                if err != nil {
3✔
910
                        return nil, err
×
911
                }
×
912

913
                // If the amount was not included in the invoice, then we let
914
                // the payer specify the amount of satoshis they wish to send.
915
                // We override the amount to pay with the amount provided from
916
                // the payment request.
917
                if payReq.MilliSat == nil {
3✔
918
                        if reqAmt == 0 {
×
919
                                return nil, errors.New("amount must be " +
×
920
                                        "specified when paying a zero amount " +
×
921
                                        "invoice")
×
922
                        }
×
923

924
                        payIntent.Amount = reqAmt
×
925
                } else {
3✔
926
                        if reqAmt != 0 {
3✔
927
                                return nil, errors.New("amount must not be " +
×
928
                                        "specified when paying a non-zero " +
×
929
                                        " amount invoice")
×
930
                        }
×
931

932
                        payIntent.Amount = *payReq.MilliSat
3✔
933
                }
934

935
                if !payReq.Features.HasFeature(lnwire.MPPOptional) &&
3✔
936
                        !payReq.Features.HasFeature(lnwire.AMPOptional) {
3✔
937

×
938
                        payIntent.MaxParts = 1
×
939
                }
×
940

941
                payAddr := payReq.PaymentAddr
3✔
942
                if payReq.Features.HasFeature(lnwire.AMPOptional) {
6✔
943
                        // The opt-in AMP flag is required to pay an AMP
3✔
944
                        // invoice.
3✔
945
                        if !rpcPayReq.Amp {
3✔
946
                                return nil, fmt.Errorf("the AMP flag (--amp " +
×
947
                                        "or SendPaymentRequest.Amp) must be " +
×
948
                                        "set to pay an AMP invoice")
×
949
                        }
×
950

951
                        // Generate random SetID and root share.
952
                        var setID [32]byte
3✔
953
                        _, err = rand.Read(setID[:])
3✔
954
                        if err != nil {
3✔
955
                                return nil, err
×
956
                        }
×
957

958
                        var rootShare [32]byte
3✔
959
                        _, err = rand.Read(rootShare[:])
3✔
960
                        if err != nil {
3✔
961
                                return nil, err
×
962
                        }
×
963
                        err := payIntent.SetAMP(&routing.AMPOptions{
3✔
964
                                SetID:     setID,
3✔
965
                                RootShare: rootShare,
3✔
966
                        })
3✔
967
                        if err != nil {
3✔
968
                                return nil, err
×
969
                        }
×
970

971
                        // For AMP invoices, we'll allow users to override the
972
                        // included payment addr to allow the invoice to be
973
                        // pseudo-reusable, e.g. the invoice parameters are
974
                        // reused (amt, cltv, hop hints, etc) even though the
975
                        // payments will share different payment hashes.
976
                        //
977
                        // NOTE: This will only work when the peer has
978
                        // spontaneous AMP payments enabled.
979
                        if len(rpcPayReq.PaymentAddr) > 0 {
6✔
980
                                var addr [32]byte
3✔
981
                                copy(addr[:], rpcPayReq.PaymentAddr)
3✔
982
                                payAddr = &addr
3✔
983
                        }
3✔
984
                } else {
3✔
985
                        err = payIntent.SetPaymentHash(*payReq.PaymentHash)
3✔
986
                        if err != nil {
3✔
987
                                return nil, err
×
988
                        }
×
989
                }
990

991
                destKey := payReq.Destination.SerializeCompressed()
3✔
992
                copy(payIntent.Target[:], destKey)
3✔
993

3✔
994
                payIntent.FinalCLTVDelta = uint16(payReq.MinFinalCLTVExpiry())
3✔
995
                payIntent.RouteHints = append(
3✔
996
                        payIntent.RouteHints, payReq.RouteHints...,
3✔
997
                )
3✔
998
                payIntent.DestFeatures = payReq.Features
3✔
999
                payIntent.PaymentAddr = payAddr
3✔
1000
                payIntent.PaymentRequest = []byte(rpcPayReq.PaymentRequest)
3✔
1001
                payIntent.Metadata = payReq.Metadata
3✔
1002

3✔
1003
                if len(payReq.BlindedPaymentPaths) > 0 {
6✔
1004
                        pathSet, err := BuildBlindedPathSet(
3✔
1005
                                payReq.BlindedPaymentPaths,
3✔
1006
                        )
3✔
1007
                        if err != nil {
3✔
NEW
1008
                                return nil, err
×
UNCOV
1009
                        }
×
1010
                        payIntent.BlindedPathSet = pathSet
3✔
1011

3✔
1012
                        // Replace the target node with the target public key
3✔
1013
                        // of the blinded path set.
3✔
1014
                        copy(
3✔
1015
                                payIntent.Target[:],
3✔
1016
                                pathSet.TargetPubKey().SerializeCompressed(),
3✔
1017
                        )
3✔
1018

3✔
1019
                        pathFeatures := pathSet.Features()
3✔
1020
                        if !pathFeatures.IsEmpty() {
3✔
NEW
1021
                                payIntent.DestFeatures = pathFeatures.Clone()
×
UNCOV
1022
                        }
×
1023
                }
1024
        } else {
3✔
1025
                // Otherwise, If the payment request field was not specified
3✔
1026
                // (and a custom route wasn't specified), construct the payment
3✔
1027
                // from the other fields.
3✔
1028

3✔
1029
                // Payment destination.
3✔
1030
                target, err := route.NewVertexFromBytes(rpcPayReq.Dest)
3✔
1031
                if err != nil {
3✔
1032
                        return nil, err
×
1033
                }
×
1034
                payIntent.Target = target
3✔
1035

3✔
1036
                // Final payment CLTV delta.
3✔
1037
                if rpcPayReq.FinalCltvDelta != 0 {
6✔
1038
                        payIntent.FinalCLTVDelta =
3✔
1039
                                uint16(rpcPayReq.FinalCltvDelta)
3✔
1040
                } else {
6✔
1041
                        payIntent.FinalCLTVDelta = r.DefaultFinalCltvDelta
3✔
1042
                }
3✔
1043

1044
                // Amount.
1045
                if reqAmt == 0 {
3✔
1046
                        return nil, errors.New("amount must be specified")
×
1047
                }
×
1048

1049
                payIntent.Amount = reqAmt
3✔
1050

3✔
1051
                // Parse destination feature bits.
3✔
1052
                features, err := UnmarshalFeatures(rpcPayReq.DestFeatures)
3✔
1053
                if err != nil {
3✔
1054
                        return nil, err
×
1055
                }
×
1056

1057
                // Validate the features if any was specified.
1058
                if features != nil {
6✔
1059
                        err = feature.ValidateDeps(features)
3✔
1060
                        if err != nil {
3✔
1061
                                return nil, err
×
1062
                        }
×
1063
                }
1064

1065
                // If this is an AMP payment, we must generate the initial
1066
                // randomness.
1067
                if rpcPayReq.Amp {
6✔
1068
                        // If no destination features were specified, we set
3✔
1069
                        // those necessary for AMP payments.
3✔
1070
                        if features == nil {
6✔
1071
                                ampFeatures := []lnrpc.FeatureBit{
3✔
1072
                                        lnrpc.FeatureBit_TLV_ONION_OPT,
3✔
1073
                                        lnrpc.FeatureBit_PAYMENT_ADDR_OPT,
3✔
1074
                                        lnrpc.FeatureBit_AMP_OPT,
3✔
1075
                                }
3✔
1076

3✔
1077
                                features, err = UnmarshalFeatures(ampFeatures)
3✔
1078
                                if err != nil {
3✔
1079
                                        return nil, err
×
1080
                                }
×
1081
                        }
1082

1083
                        // First make sure the destination supports AMP.
1084
                        if !features.HasFeature(lnwire.AMPOptional) {
3✔
1085
                                return nil, fmt.Errorf("destination doesn't " +
×
1086
                                        "support AMP payments")
×
1087
                        }
×
1088

1089
                        // If no payment address is set, generate a random one.
1090
                        var payAddr [32]byte
3✔
1091
                        if len(rpcPayReq.PaymentAddr) == 0 {
6✔
1092
                                _, err = rand.Read(payAddr[:])
3✔
1093
                                if err != nil {
3✔
1094
                                        return nil, err
×
1095
                                }
×
1096
                        } else {
×
1097
                                copy(payAddr[:], rpcPayReq.PaymentAddr)
×
1098
                        }
×
1099
                        payIntent.PaymentAddr = &payAddr
3✔
1100

3✔
1101
                        // Generate random SetID and root share.
3✔
1102
                        var setID [32]byte
3✔
1103
                        _, err = rand.Read(setID[:])
3✔
1104
                        if err != nil {
3✔
1105
                                return nil, err
×
1106
                        }
×
1107

1108
                        var rootShare [32]byte
3✔
1109
                        _, err = rand.Read(rootShare[:])
3✔
1110
                        if err != nil {
3✔
1111
                                return nil, err
×
1112
                        }
×
1113
                        err := payIntent.SetAMP(&routing.AMPOptions{
3✔
1114
                                SetID:     setID,
3✔
1115
                                RootShare: rootShare,
3✔
1116
                        })
3✔
1117
                        if err != nil {
3✔
1118
                                return nil, err
×
1119
                        }
×
1120
                } else {
3✔
1121
                        // Payment hash.
3✔
1122
                        paymentHash, err := lntypes.MakeHash(rpcPayReq.PaymentHash)
3✔
1123
                        if err != nil {
3✔
1124
                                return nil, err
×
1125
                        }
×
1126

1127
                        err = payIntent.SetPaymentHash(paymentHash)
3✔
1128
                        if err != nil {
3✔
1129
                                return nil, err
×
1130
                        }
×
1131

1132
                        // If the payment addresses is specified, then we'll
1133
                        // also populate that now as well.
1134
                        if len(rpcPayReq.PaymentAddr) != 0 {
6✔
1135
                                var payAddr [32]byte
3✔
1136
                                copy(payAddr[:], rpcPayReq.PaymentAddr)
3✔
1137

3✔
1138
                                payIntent.PaymentAddr = &payAddr
3✔
1139
                        }
3✔
1140
                }
1141

1142
                payIntent.DestFeatures = features
3✔
1143
        }
1144

1145
        // Do bounds checking with the block padding so the router isn't
1146
        // left with a zombie payment in case the user messes up.
1147
        err = routing.ValidateCLTVLimit(
3✔
1148
                payIntent.CltvLimit, payIntent.FinalCLTVDelta, true,
3✔
1149
        )
3✔
1150
        if err != nil {
3✔
1151
                return nil, err
×
1152
        }
×
1153

1154
        // Check for disallowed payments to self.
1155
        if !rpcPayReq.AllowSelfPayment && payIntent.Target == r.SelfNode {
3✔
1156
                return nil, errors.New("self-payments not allowed")
×
1157
        }
×
1158

1159
        return payIntent, nil
3✔
1160
}
1161

1162
// BuildBlindedPathSet marshals a set of zpay32.BlindedPaymentPath and uses
1163
// the result to build a new routing.BlindedPaymentPathSet.
1164
func BuildBlindedPathSet(paths []*zpay32.BlindedPaymentPath) (
1165
        *routing.BlindedPaymentPathSet, error) {
3✔
1166

3✔
1167
        marshalledPaths := make([]*routing.BlindedPayment, len(paths))
3✔
1168
        for i, path := range paths {
6✔
1169
                paymentPath := marshalBlindedPayment(path)
3✔
1170

3✔
1171
                err := paymentPath.Validate()
3✔
1172
                if err != nil {
3✔
NEW
1173
                        return nil, err
×
NEW
1174
                }
×
1175

1176
                marshalledPaths[i] = paymentPath
3✔
1177
        }
1178

1179
        return routing.NewBlindedPaymentPathSet(marshalledPaths)
3✔
1180
}
1181

1182
// marshalBlindedPayment marshals a zpay32.BLindedPaymentPath into a
1183
// routing.BlindedPayment.
1184
func marshalBlindedPayment(
1185
        path *zpay32.BlindedPaymentPath) *routing.BlindedPayment {
3✔
1186

3✔
1187
        return &routing.BlindedPayment{
3✔
1188
                BlindedPath: &sphinx.BlindedPath{
3✔
1189
                        IntroductionPoint: path.Hops[0].BlindedNodePub,
3✔
1190
                        BlindingPoint:     path.FirstEphemeralBlindingPoint,
3✔
1191
                        BlindedHops:       path.Hops,
3✔
1192
                },
3✔
1193
                BaseFee:             path.FeeBaseMsat,
3✔
1194
                ProportionalFeeRate: path.FeeRate,
3✔
1195
                CltvExpiryDelta:     path.CltvExpiryDelta,
3✔
1196
                HtlcMinimum:         path.HTLCMinMsat,
3✔
1197
                HtlcMaximum:         path.HTLCMaxMsat,
3✔
1198
                Features:            path.Features,
3✔
1199
        }
3✔
1200
}
3✔
1201

1202
// unmarshallRouteHints unmarshalls a list of route hints.
1203
func unmarshallRouteHints(rpcRouteHints []*lnrpc.RouteHint) (
1204
        [][]zpay32.HopHint, error) {
3✔
1205

3✔
1206
        routeHints := make([][]zpay32.HopHint, 0, len(rpcRouteHints))
3✔
1207
        for _, rpcRouteHint := range rpcRouteHints {
6✔
1208
                routeHint := make(
3✔
1209
                        []zpay32.HopHint, 0, len(rpcRouteHint.HopHints),
3✔
1210
                )
3✔
1211
                for _, rpcHint := range rpcRouteHint.HopHints {
6✔
1212
                        hint, err := unmarshallHopHint(rpcHint)
3✔
1213
                        if err != nil {
3✔
1214
                                return nil, err
×
1215
                        }
×
1216

1217
                        routeHint = append(routeHint, hint)
3✔
1218
                }
1219
                routeHints = append(routeHints, routeHint)
3✔
1220
        }
1221

1222
        return routeHints, nil
3✔
1223
}
1224

1225
// unmarshallHopHint unmarshalls a single hop hint.
1226
func unmarshallHopHint(rpcHint *lnrpc.HopHint) (zpay32.HopHint, error) {
3✔
1227
        pubBytes, err := hex.DecodeString(rpcHint.NodeId)
3✔
1228
        if err != nil {
3✔
1229
                return zpay32.HopHint{}, err
×
1230
        }
×
1231

1232
        pubkey, err := btcec.ParsePubKey(pubBytes)
3✔
1233
        if err != nil {
3✔
1234
                return zpay32.HopHint{}, err
×
1235
        }
×
1236

1237
        return zpay32.HopHint{
3✔
1238
                NodeID:                    pubkey,
3✔
1239
                ChannelID:                 rpcHint.ChanId,
3✔
1240
                FeeBaseMSat:               rpcHint.FeeBaseMsat,
3✔
1241
                FeeProportionalMillionths: rpcHint.FeeProportionalMillionths,
3✔
1242
                CLTVExpiryDelta:           uint16(rpcHint.CltvExpiryDelta),
3✔
1243
        }, nil
3✔
1244
}
1245

1246
// MarshalFeatures converts a feature vector into a list of uint32's.
1247
func MarshalFeatures(feats *lnwire.FeatureVector) []lnrpc.FeatureBit {
3✔
1248
        var featureBits []lnrpc.FeatureBit
3✔
1249
        for feature := range feats.Features() {
6✔
1250
                featureBits = append(featureBits, lnrpc.FeatureBit(feature))
3✔
1251
        }
3✔
1252

1253
        return featureBits
3✔
1254
}
1255

1256
// UnmarshalFeatures converts a list of uint32's into a valid feature vector.
1257
// This method checks that feature bit pairs aren't assigned together, and
1258
// validates transitive dependencies.
1259
func UnmarshalFeatures(
1260
        rpcFeatures []lnrpc.FeatureBit) (*lnwire.FeatureVector, error) {
3✔
1261

3✔
1262
        // If no destination features are specified we'll return nil to signal
3✔
1263
        // that the router should try to use the graph as a fallback.
3✔
1264
        if rpcFeatures == nil {
6✔
1265
                return nil, nil
3✔
1266
        }
3✔
1267

1268
        raw := lnwire.NewRawFeatureVector()
3✔
1269
        for _, bit := range rpcFeatures {
6✔
1270
                err := raw.SafeSet(lnwire.FeatureBit(bit))
3✔
1271
                if err != nil {
3✔
1272
                        return nil, err
×
1273
                }
×
1274
        }
1275

1276
        return lnwire.NewFeatureVector(raw, lnwire.Features), nil
3✔
1277
}
1278

1279
// ValidatePayReqExpiry checks if the passed payment request has expired. In
1280
// the case it has expired, an error will be returned.
1281
func ValidatePayReqExpiry(payReq *zpay32.Invoice) error {
3✔
1282
        expiry := payReq.Expiry()
3✔
1283
        validUntil := payReq.Timestamp.Add(expiry)
3✔
1284
        if time.Now().After(validUntil) {
3✔
1285
                return fmt.Errorf("invoice expired. Valid until %v", validUntil)
×
1286
        }
×
1287

1288
        return nil
3✔
1289
}
1290

1291
// ValidateCLTVLimit returns a valid CLTV limit given a value and a maximum. If
1292
// the value exceeds the maximum, then an error is returned. If the value is 0,
1293
// then the maximum is used.
1294
func ValidateCLTVLimit(val, max uint32) (uint32, error) {
3✔
1295
        switch {
3✔
1296
        case val == 0:
3✔
1297
                return max, nil
3✔
1298
        case val > max:
×
1299
                return 0, fmt.Errorf("total time lock of %v exceeds max "+
×
1300
                        "allowed %v", val, max)
×
1301
        default:
×
1302
                return val, nil
×
1303
        }
1304
}
1305

1306
// UnmarshalMPP accepts the mpp_total_amt_msat and mpp_payment_addr fields from
1307
// an RPC request and converts into an record.MPP object. An error is returned
1308
// if the payment address is not 0 or 32 bytes. If the total amount and payment
1309
// address are zero-value, the return value will be nil signaling there is no
1310
// MPP record to attach to this hop. Otherwise, a non-nil reocrd will be
1311
// contained combining the provided values.
1312
func UnmarshalMPP(reqMPP *lnrpc.MPPRecord) (*record.MPP, error) {
3✔
1313
        // If no MPP record was submitted, assume the user wants to send a
3✔
1314
        // regular payment.
3✔
1315
        if reqMPP == nil {
6✔
1316
                return nil, nil
3✔
1317
        }
3✔
1318

1319
        reqTotal := reqMPP.TotalAmtMsat
3✔
1320
        reqAddr := reqMPP.PaymentAddr
3✔
1321

3✔
1322
        switch {
3✔
1323
        // No MPP fields were provided.
1324
        case reqTotal == 0 && len(reqAddr) == 0:
×
1325
                return nil, fmt.Errorf("missing total_msat and payment_addr")
×
1326

1327
        // Total is present, but payment address is missing.
1328
        case reqTotal > 0 && len(reqAddr) == 0:
×
1329
                return nil, fmt.Errorf("missing payment_addr")
×
1330

1331
        // Payment address is present, but total is missing.
1332
        case reqTotal == 0 && len(reqAddr) > 0:
×
1333
                return nil, fmt.Errorf("missing total_msat")
×
1334
        }
1335

1336
        addr, err := lntypes.MakeHash(reqAddr)
3✔
1337
        if err != nil {
3✔
1338
                return nil, fmt.Errorf("unable to parse "+
×
1339
                        "payment_addr: %v", err)
×
1340
        }
×
1341

1342
        total := lnwire.MilliSatoshi(reqTotal)
3✔
1343

3✔
1344
        return record.NewMPP(total, addr), nil
3✔
1345
}
1346

1347
func UnmarshalAMP(reqAMP *lnrpc.AMPRecord) (*record.AMP, error) {
3✔
1348
        if reqAMP == nil {
6✔
1349
                return nil, nil
3✔
1350
        }
3✔
1351

1352
        reqRootShare := reqAMP.RootShare
3✔
1353
        reqSetID := reqAMP.SetId
3✔
1354

3✔
1355
        switch {
3✔
1356
        case len(reqRootShare) != 32:
×
1357
                return nil, errors.New("AMP root_share must be 32 bytes")
×
1358

1359
        case len(reqSetID) != 32:
×
1360
                return nil, errors.New("AMP set_id must be 32 bytes")
×
1361
        }
1362

1363
        var (
3✔
1364
                rootShare [32]byte
3✔
1365
                setID     [32]byte
3✔
1366
        )
3✔
1367
        copy(rootShare[:], reqRootShare)
3✔
1368
        copy(setID[:], reqSetID)
3✔
1369

3✔
1370
        return record.NewAMP(rootShare, setID, reqAMP.ChildIndex), nil
3✔
1371
}
1372

1373
// MarshalHTLCAttempt constructs an RPC HTLCAttempt from the db representation.
1374
func (r *RouterBackend) MarshalHTLCAttempt(
1375
        htlc channeldb.HTLCAttempt) (*lnrpc.HTLCAttempt, error) {
3✔
1376

3✔
1377
        route, err := r.MarshallRoute(&htlc.Route)
3✔
1378
        if err != nil {
3✔
1379
                return nil, err
×
1380
        }
×
1381

1382
        rpcAttempt := &lnrpc.HTLCAttempt{
3✔
1383
                AttemptId:     htlc.AttemptID,
3✔
1384
                AttemptTimeNs: MarshalTimeNano(htlc.AttemptTime),
3✔
1385
                Route:         route,
3✔
1386
        }
3✔
1387

3✔
1388
        switch {
3✔
1389
        case htlc.Settle != nil:
3✔
1390
                rpcAttempt.Status = lnrpc.HTLCAttempt_SUCCEEDED
3✔
1391
                rpcAttempt.ResolveTimeNs = MarshalTimeNano(
3✔
1392
                        htlc.Settle.SettleTime,
3✔
1393
                )
3✔
1394
                rpcAttempt.Preimage = htlc.Settle.Preimage[:]
3✔
1395

1396
        case htlc.Failure != nil:
3✔
1397
                rpcAttempt.Status = lnrpc.HTLCAttempt_FAILED
3✔
1398
                rpcAttempt.ResolveTimeNs = MarshalTimeNano(
3✔
1399
                        htlc.Failure.FailTime,
3✔
1400
                )
3✔
1401

3✔
1402
                var err error
3✔
1403
                rpcAttempt.Failure, err = marshallHtlcFailure(htlc.Failure)
3✔
1404
                if err != nil {
3✔
1405
                        return nil, err
×
1406
                }
×
1407
        default:
3✔
1408
                rpcAttempt.Status = lnrpc.HTLCAttempt_IN_FLIGHT
3✔
1409
        }
1410

1411
        return rpcAttempt, nil
3✔
1412
}
1413

1414
// marshallHtlcFailure marshalls htlc fail info from the database to its rpc
1415
// representation.
1416
func marshallHtlcFailure(failure *channeldb.HTLCFailInfo) (*lnrpc.Failure,
1417
        error) {
3✔
1418

3✔
1419
        rpcFailure := &lnrpc.Failure{
3✔
1420
                FailureSourceIndex: failure.FailureSourceIndex,
3✔
1421
        }
3✔
1422

3✔
1423
        switch failure.Reason {
3✔
1424
        case channeldb.HTLCFailUnknown:
×
1425
                rpcFailure.Code = lnrpc.Failure_UNKNOWN_FAILURE
×
1426

1427
        case channeldb.HTLCFailUnreadable:
×
1428
                rpcFailure.Code = lnrpc.Failure_UNREADABLE_FAILURE
×
1429

1430
        case channeldb.HTLCFailInternal:
×
1431
                rpcFailure.Code = lnrpc.Failure_INTERNAL_FAILURE
×
1432

1433
        case channeldb.HTLCFailMessage:
3✔
1434
                err := marshallWireError(failure.Message, rpcFailure)
3✔
1435
                if err != nil {
3✔
1436
                        return nil, err
×
1437
                }
×
1438

1439
        default:
×
1440
                return nil, errors.New("unknown htlc failure reason")
×
1441
        }
1442

1443
        return rpcFailure, nil
3✔
1444
}
1445

1446
// MarshalTimeNano converts a time.Time into its nanosecond representation. If
1447
// the time is zero, this method simply returns 0, since calling UnixNano() on a
1448
// zero-valued time is undefined.
1449
func MarshalTimeNano(t time.Time) int64 {
3✔
1450
        if t.IsZero() {
3✔
1451
                return 0
×
1452
        }
×
1453
        return t.UnixNano()
3✔
1454
}
1455

1456
// marshallError marshall an error as received from the switch to rpc structs
1457
// suitable for returning to the caller of an rpc method.
1458
//
1459
// Because of difficulties with using protobuf oneof constructs in some
1460
// languages, the decision was made here to use a single message format for all
1461
// failure messages with some fields left empty depending on the failure type.
1462
func marshallError(sendError error) (*lnrpc.Failure, error) {
3✔
1463
        response := &lnrpc.Failure{}
3✔
1464

3✔
1465
        if sendError == htlcswitch.ErrUnreadableFailureMessage {
3✔
1466
                response.Code = lnrpc.Failure_UNREADABLE_FAILURE
×
1467
                return response, nil
×
1468
        }
×
1469

1470
        rtErr, ok := sendError.(htlcswitch.ClearTextError)
3✔
1471
        if !ok {
3✔
1472
                return nil, sendError
×
1473
        }
×
1474

1475
        err := marshallWireError(rtErr.WireMessage(), response)
3✔
1476
        if err != nil {
3✔
1477
                return nil, err
×
1478
        }
×
1479

1480
        // If the ClearTextError received is a ForwardingError, the error
1481
        // originated from a node along the route, not locally on our outgoing
1482
        // link. We set failureSourceIdx to the index of the node where the
1483
        // failure occurred. If the error is not a ForwardingError, the failure
1484
        // occurred at our node, so we leave the index as 0 to indicate that
1485
        // we failed locally.
1486
        fErr, ok := rtErr.(*htlcswitch.ForwardingError)
3✔
1487
        if ok {
3✔
1488
                response.FailureSourceIndex = uint32(fErr.FailureSourceIdx)
×
1489
        }
×
1490

1491
        return response, nil
3✔
1492
}
1493

1494
// marshallError marshall an error as received from the switch to rpc structs
1495
// suitable for returning to the caller of an rpc method.
1496
//
1497
// Because of difficulties with using protobuf oneof constructs in some
1498
// languages, the decision was made here to use a single message format for all
1499
// failure messages with some fields left empty depending on the failure type.
1500
func marshallWireError(msg lnwire.FailureMessage,
1501
        response *lnrpc.Failure) error {
3✔
1502

3✔
1503
        switch onionErr := msg.(type) {
3✔
1504
        case *lnwire.FailIncorrectDetails:
3✔
1505
                response.Code = lnrpc.Failure_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS
3✔
1506
                response.Height = onionErr.Height()
3✔
1507

1508
        case *lnwire.FailIncorrectPaymentAmount:
×
1509
                response.Code = lnrpc.Failure_INCORRECT_PAYMENT_AMOUNT
×
1510

1511
        case *lnwire.FailFinalIncorrectCltvExpiry:
×
1512
                response.Code = lnrpc.Failure_FINAL_INCORRECT_CLTV_EXPIRY
×
1513
                response.CltvExpiry = onionErr.CltvExpiry
×
1514

1515
        case *lnwire.FailFinalIncorrectHtlcAmount:
×
1516
                response.Code = lnrpc.Failure_FINAL_INCORRECT_HTLC_AMOUNT
×
1517
                response.HtlcMsat = uint64(onionErr.IncomingHTLCAmount)
×
1518

1519
        case *lnwire.FailFinalExpiryTooSoon:
×
1520
                response.Code = lnrpc.Failure_FINAL_EXPIRY_TOO_SOON
×
1521

1522
        case *lnwire.FailInvalidRealm:
×
1523
                response.Code = lnrpc.Failure_INVALID_REALM
×
1524

1525
        case *lnwire.FailExpiryTooSoon:
×
1526
                response.Code = lnrpc.Failure_EXPIRY_TOO_SOON
×
1527
                response.ChannelUpdate = marshallChannelUpdate(&onionErr.Update)
×
1528

1529
        case *lnwire.FailExpiryTooFar:
×
1530
                response.Code = lnrpc.Failure_EXPIRY_TOO_FAR
×
1531

1532
        case *lnwire.FailInvalidOnionVersion:
3✔
1533
                response.Code = lnrpc.Failure_INVALID_ONION_VERSION
3✔
1534
                response.OnionSha_256 = onionErr.OnionSHA256[:]
3✔
1535

1536
        case *lnwire.FailInvalidOnionHmac:
×
1537
                response.Code = lnrpc.Failure_INVALID_ONION_HMAC
×
1538
                response.OnionSha_256 = onionErr.OnionSHA256[:]
×
1539

1540
        case *lnwire.FailInvalidOnionKey:
×
1541
                response.Code = lnrpc.Failure_INVALID_ONION_KEY
×
1542
                response.OnionSha_256 = onionErr.OnionSHA256[:]
×
1543

1544
        case *lnwire.FailAmountBelowMinimum:
×
1545
                response.Code = lnrpc.Failure_AMOUNT_BELOW_MINIMUM
×
1546
                response.ChannelUpdate = marshallChannelUpdate(&onionErr.Update)
×
1547
                response.HtlcMsat = uint64(onionErr.HtlcMsat)
×
1548

1549
        case *lnwire.FailFeeInsufficient:
3✔
1550
                response.Code = lnrpc.Failure_FEE_INSUFFICIENT
3✔
1551
                response.ChannelUpdate = marshallChannelUpdate(&onionErr.Update)
3✔
1552
                response.HtlcMsat = uint64(onionErr.HtlcMsat)
3✔
1553

1554
        case *lnwire.FailIncorrectCltvExpiry:
×
1555
                response.Code = lnrpc.Failure_INCORRECT_CLTV_EXPIRY
×
1556
                response.ChannelUpdate = marshallChannelUpdate(&onionErr.Update)
×
1557
                response.CltvExpiry = onionErr.CltvExpiry
×
1558

1559
        case *lnwire.FailChannelDisabled:
3✔
1560
                response.Code = lnrpc.Failure_CHANNEL_DISABLED
3✔
1561
                response.ChannelUpdate = marshallChannelUpdate(&onionErr.Update)
3✔
1562
                response.Flags = uint32(onionErr.Flags)
3✔
1563

1564
        case *lnwire.FailTemporaryChannelFailure:
3✔
1565
                response.Code = lnrpc.Failure_TEMPORARY_CHANNEL_FAILURE
3✔
1566
                response.ChannelUpdate = marshallChannelUpdate(onionErr.Update)
3✔
1567

1568
        case *lnwire.FailRequiredNodeFeatureMissing:
×
1569
                response.Code = lnrpc.Failure_REQUIRED_NODE_FEATURE_MISSING
×
1570

1571
        case *lnwire.FailRequiredChannelFeatureMissing:
×
1572
                response.Code = lnrpc.Failure_REQUIRED_CHANNEL_FEATURE_MISSING
×
1573

1574
        case *lnwire.FailUnknownNextPeer:
3✔
1575
                response.Code = lnrpc.Failure_UNKNOWN_NEXT_PEER
3✔
1576

1577
        case *lnwire.FailTemporaryNodeFailure:
×
1578
                response.Code = lnrpc.Failure_TEMPORARY_NODE_FAILURE
×
1579

1580
        case *lnwire.FailPermanentNodeFailure:
×
1581
                response.Code = lnrpc.Failure_PERMANENT_NODE_FAILURE
×
1582

1583
        case *lnwire.FailPermanentChannelFailure:
3✔
1584
                response.Code = lnrpc.Failure_PERMANENT_CHANNEL_FAILURE
3✔
1585

1586
        case *lnwire.FailMPPTimeout:
×
1587
                response.Code = lnrpc.Failure_MPP_TIMEOUT
×
1588

1589
        case *lnwire.InvalidOnionPayload:
3✔
1590
                response.Code = lnrpc.Failure_INVALID_ONION_PAYLOAD
3✔
1591

1592
        case *lnwire.FailInvalidBlinding:
3✔
1593
                response.Code = lnrpc.Failure_INVALID_ONION_BLINDING
3✔
1594
                response.OnionSha_256 = onionErr.OnionSHA256[:]
3✔
1595

1596
        case nil:
×
1597
                response.Code = lnrpc.Failure_UNKNOWN_FAILURE
×
1598

1599
        default:
×
1600
                return fmt.Errorf("cannot marshall failure %T", onionErr)
×
1601
        }
1602

1603
        return nil
3✔
1604
}
1605

1606
// marshallChannelUpdate marshalls a channel update as received over the wire to
1607
// the router rpc format.
1608
func marshallChannelUpdate(update *lnwire.ChannelUpdate) *lnrpc.ChannelUpdate {
3✔
1609
        if update == nil {
3✔
1610
                return nil
×
1611
        }
×
1612

1613
        return &lnrpc.ChannelUpdate{
3✔
1614
                Signature:       update.Signature.RawBytes(),
3✔
1615
                ChainHash:       update.ChainHash[:],
3✔
1616
                ChanId:          update.ShortChannelID.ToUint64(),
3✔
1617
                Timestamp:       update.Timestamp,
3✔
1618
                MessageFlags:    uint32(update.MessageFlags),
3✔
1619
                ChannelFlags:    uint32(update.ChannelFlags),
3✔
1620
                TimeLockDelta:   uint32(update.TimeLockDelta),
3✔
1621
                HtlcMinimumMsat: uint64(update.HtlcMinimumMsat),
3✔
1622
                BaseFee:         update.BaseFee,
3✔
1623
                FeeRate:         update.FeeRate,
3✔
1624
                HtlcMaximumMsat: uint64(update.HtlcMaximumMsat),
3✔
1625
                ExtraOpaqueData: update.ExtraOpaqueData,
3✔
1626
        }
3✔
1627
}
1628

1629
// MarshallPayment marshall a payment to its rpc representation.
1630
func (r *RouterBackend) MarshallPayment(payment *channeldb.MPPayment) (
1631
        *lnrpc.Payment, error) {
3✔
1632

3✔
1633
        // Fetch the payment's preimage and the total paid in fees.
3✔
1634
        var (
3✔
1635
                fee      lnwire.MilliSatoshi
3✔
1636
                preimage lntypes.Preimage
3✔
1637
        )
3✔
1638
        for _, htlc := range payment.HTLCs {
6✔
1639
                // If any of the htlcs have settled, extract a valid
3✔
1640
                // preimage.
3✔
1641
                if htlc.Settle != nil {
6✔
1642
                        preimage = htlc.Settle.Preimage
3✔
1643
                        fee += htlc.Route.TotalFees()
3✔
1644
                }
3✔
1645
        }
1646

1647
        msatValue := int64(payment.Info.Value)
3✔
1648
        satValue := int64(payment.Info.Value.ToSatoshis())
3✔
1649

3✔
1650
        status, err := convertPaymentStatus(
3✔
1651
                payment.Status, r.UseStatusInitiated,
3✔
1652
        )
3✔
1653
        if err != nil {
3✔
1654
                return nil, err
×
1655
        }
×
1656

1657
        htlcs := make([]*lnrpc.HTLCAttempt, 0, len(payment.HTLCs))
3✔
1658
        for _, dbHTLC := range payment.HTLCs {
6✔
1659
                htlc, err := r.MarshalHTLCAttempt(dbHTLC)
3✔
1660
                if err != nil {
3✔
1661
                        return nil, err
×
1662
                }
×
1663

1664
                htlcs = append(htlcs, htlc)
3✔
1665
        }
1666

1667
        paymentID := payment.Info.PaymentIdentifier
3✔
1668
        creationTimeNS := MarshalTimeNano(payment.Info.CreationTime)
3✔
1669

3✔
1670
        failureReason, err := marshallPaymentFailureReason(
3✔
1671
                payment.FailureReason,
3✔
1672
        )
3✔
1673
        if err != nil {
3✔
1674
                return nil, err
×
1675
        }
×
1676

1677
        return &lnrpc.Payment{
3✔
1678
                // TODO: set this to setID for AMP-payments?
3✔
1679
                PaymentHash:     hex.EncodeToString(paymentID[:]),
3✔
1680
                Value:           satValue,
3✔
1681
                ValueMsat:       msatValue,
3✔
1682
                ValueSat:        satValue,
3✔
1683
                CreationDate:    payment.Info.CreationTime.Unix(),
3✔
1684
                CreationTimeNs:  creationTimeNS,
3✔
1685
                Fee:             int64(fee.ToSatoshis()),
3✔
1686
                FeeSat:          int64(fee.ToSatoshis()),
3✔
1687
                FeeMsat:         int64(fee),
3✔
1688
                PaymentPreimage: hex.EncodeToString(preimage[:]),
3✔
1689
                PaymentRequest:  string(payment.Info.PaymentRequest),
3✔
1690
                Status:          status,
3✔
1691
                Htlcs:           htlcs,
3✔
1692
                PaymentIndex:    payment.SequenceNum,
3✔
1693
                FailureReason:   failureReason,
3✔
1694
        }, nil
3✔
1695
}
1696

1697
// convertPaymentStatus converts a channeldb.PaymentStatus to the type expected
1698
// by the RPC.
1699
func convertPaymentStatus(dbStatus channeldb.PaymentStatus, useInit bool) (
1700
        lnrpc.Payment_PaymentStatus, error) {
3✔
1701

3✔
1702
        switch dbStatus {
3✔
1703
        case channeldb.StatusInitiated:
3✔
1704
                // If the client understands the new status, return it.
3✔
1705
                if useInit {
6✔
1706
                        return lnrpc.Payment_INITIATED, nil
3✔
1707
                }
3✔
1708

1709
                // Otherwise remain the old behavior.
1710
                return lnrpc.Payment_IN_FLIGHT, nil
3✔
1711

1712
        case channeldb.StatusInFlight:
3✔
1713
                return lnrpc.Payment_IN_FLIGHT, nil
3✔
1714

1715
        case channeldb.StatusSucceeded:
3✔
1716
                return lnrpc.Payment_SUCCEEDED, nil
3✔
1717

1718
        case channeldb.StatusFailed:
3✔
1719
                return lnrpc.Payment_FAILED, nil
3✔
1720

1721
        default:
×
1722
                return 0, fmt.Errorf("unhandled payment status %v", dbStatus)
×
1723
        }
1724
}
1725

1726
// marshallPaymentFailureReason marshalls the failure reason to the corresponding rpc
1727
// type.
1728
func marshallPaymentFailureReason(reason *channeldb.FailureReason) (
1729
        lnrpc.PaymentFailureReason, error) {
3✔
1730

3✔
1731
        if reason == nil {
6✔
1732
                return lnrpc.PaymentFailureReason_FAILURE_REASON_NONE, nil
3✔
1733
        }
3✔
1734

1735
        switch *reason {
3✔
1736
        case channeldb.FailureReasonTimeout:
×
1737
                return lnrpc.PaymentFailureReason_FAILURE_REASON_TIMEOUT, nil
×
1738

1739
        case channeldb.FailureReasonNoRoute:
3✔
1740
                return lnrpc.PaymentFailureReason_FAILURE_REASON_NO_ROUTE, nil
3✔
1741

1742
        case channeldb.FailureReasonError:
3✔
1743
                return lnrpc.PaymentFailureReason_FAILURE_REASON_ERROR, nil
3✔
1744

1745
        case channeldb.FailureReasonPaymentDetails:
3✔
1746
                return lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS, nil
3✔
1747

1748
        case channeldb.FailureReasonInsufficientBalance:
3✔
1749
                return lnrpc.PaymentFailureReason_FAILURE_REASON_INSUFFICIENT_BALANCE, nil
3✔
1750
        }
1751

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