Skip to content

Commit f1a7d8f

Browse files
committed
multi: pass private, routehints from loopcli - loopd - loop server
This commit passes routehints all the way from when/if the user passes them from the cli all the way to the backend loop server. If private is used, this commit passes that boolean down to different stages, where it is then converted into routehints. main: add --private and --route_hints to quote Adds --private and --route_hints flags to quote cli
1 parent 4299147 commit f1a7d8f

File tree

11 files changed

+652
-430
lines changed

11 files changed

+652
-430
lines changed

client.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/lightninglabs/loop/loopdb"
1616
"github.com/lightninglabs/loop/swap"
1717
"github.com/lightninglabs/loop/sweep"
18+
"github.com/lightningnetwork/lnd/routing/route"
1819
"google.golang.org/grpc/status"
1920
)
2021

@@ -563,6 +564,30 @@ func (s *Client) LoopInQuote(ctx context.Context,
563564
return nil, ErrSwapAmountTooHigh
564565
}
565566

567+
// Private and routehints are mutually exclusive as setting private
568+
// means we retrieve our own routehints from the connected node.
569+
if len(request.RouteHints) != 0 && request.Private {
570+
return nil, fmt.Errorf("private and route_hints both set")
571+
}
572+
573+
if request.Private {
574+
// If last_hop is set, we'll only add channels with peers
575+
// set to the last_hop parameter
576+
includeNodes := make(map[route.Vertex]struct{})
577+
if request.LastHop != nil {
578+
includeNodes[*request.LastHop] = struct{}{}
579+
}
580+
581+
// Because the Private flag is set, we'll generate our own
582+
// set of hop hints and use that
583+
request.RouteHints, err = SelectHopHints(
584+
ctx, s.lndServices, request.Amount, DefaultMaxHopHints, includeNodes,
585+
)
586+
if err != nil {
587+
return nil, err
588+
}
589+
}
590+
566591
quote, err := s.Server.GetLoopInQuote(
567592
ctx, request.Amount, s.lndServices.NodePubkey, request.LastHop,
568593
request.RouteHints,

cmd/loop/loopin.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package main
22

33
import (
44
"context"
5-
"encoding/json"
65
"fmt"
76

87
"github.com/lightninglabs/loop"
@@ -134,12 +133,12 @@ func loopIn(ctx *cli.Context) error {
134133

135134
lastHop = lastHopVertex[:]
136135
}
137-
var hints []*looprpc.RouteHint
138-
if ctx.IsSet(routeHints.Name) {
139-
err = json.Unmarshal([]byte(ctx.String(routeHints.Name)), &hints)
140-
if err != nil {
141-
return fmt.Errorf("unable to parse json: %v", err)
142-
}
136+
137+
// Private and routehints are mutually exclusive as setting private
138+
// means we retrieve our own routehints from the connected node.
139+
hints, err := validateRouteHints(ctx)
140+
if err != nil {
141+
return err
143142
}
144143

145144
quoteReq := &looprpc.QuoteRequest{
@@ -148,6 +147,7 @@ func loopIn(ctx *cli.Context) error {
148147
ExternalHtlc: external,
149148
LoopInLastHop: lastHop,
150149
LoopInRouteHints: hints,
150+
Private: ctx.Bool(privateFlag.Name),
151151
}
152152

153153
quote, err := client.GetLoopInQuote(context.Background(), quoteReq)
@@ -184,6 +184,8 @@ func loopIn(ctx *cli.Context) error {
184184
Label: label,
185185
Initiator: defaultInitiator,
186186
LastHop: lastHop,
187+
RouteHints: hints,
188+
Private: ctx.Bool(privateFlag.Name),
187189
}
188190

189191
resp, err := client.LoopIn(context.Background(), req)

cmd/loop/quote.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ var quoteInCommand = cli.Command{
3131
},
3232
confTargetFlag,
3333
verboseFlag,
34+
privateFlag,
35+
routeHintsFlag,
3436
},
3537
Action: quoteIn,
3638
}
@@ -53,9 +55,18 @@ func quoteIn(ctx *cli.Context) error {
5355
}
5456
defer cleanup()
5557

58+
// Private and routehints are mutually exclusive as setting private
59+
// means we retrieve our own routehints from the connected node.
60+
hints, err := validateRouteHints(ctx)
61+
if err != nil {
62+
return err
63+
}
64+
5665
quoteReq := &looprpc.QuoteRequest{
57-
Amt: int64(amt),
58-
ConfTarget: int32(ctx.Uint64("conf_target")),
66+
Amt: int64(amt),
67+
ConfTarget: int32(ctx.Uint64("conf_target")),
68+
LoopInRouteHints: hints,
69+
Private: ctx.Bool(privateFlag.Name),
5970
}
6071

6172
if ctx.IsSet(lastHopFlag.Name) {

cmd/loop/utils.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/lightninglabs/loop/looprpc"
8+
"github.com/urfave/cli"
9+
)
10+
11+
// validateRouteHints ensures that the Private flag isn't set along with
12+
// the RouteHints flag. We don't allow both options to be set as these options
13+
// are alternatives to each other. Private autogenerates hopHints while
14+
// RouteHints are manually passed.
15+
func validateRouteHints(ctx *cli.Context) ([]*looprpc.RouteHint, error) {
16+
var hints []*looprpc.RouteHint
17+
18+
if ctx.IsSet(routeHintsFlag.Name) {
19+
if ctx.IsSet(privateFlag.Name) {
20+
return nil, fmt.Errorf(
21+
"private and route_hints both set",
22+
)
23+
}
24+
25+
stringHints := []byte(ctx.String(routeHintsFlag.Name))
26+
err := json.Unmarshal(stringHints, &hints)
27+
if err != nil {
28+
return nil, fmt.Errorf("unable to parse json: %v", err)
29+
}
30+
}
31+
32+
return hints, nil
33+
}

interface.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,15 @@ type LoopInRequest struct {
207207
// initiated the swap (loop CLI, autolooper, LiT UI and so on) and is
208208
// appended to the user agent string.
209209
Initiator string
210+
211+
// Private indicates whether the destination node should be considered
212+
// private. In which case, loop will generate hophints to assist with
213+
// probing and payment.
214+
Private bool
215+
216+
// RouteHints are optional route hints to reach the destination through
217+
// private channels.
218+
RouteHints [][]zpay32.HopHint
210219
}
211220

212221
// LoopInTerms are the server terms on which it executes loop in swaps.
@@ -253,6 +262,11 @@ type LoopInQuoteRequest struct {
253262
// RouteHints are optional route hints to reach the destination through
254263
// private channels.
255264
RouteHints [][]zpay32.HopHint
265+
266+
// Private indicates whether the destination node should be considered
267+
// private. In which case, loop will generate hophints to assist with
268+
// probing and payment.
269+
Private bool
256270
}
257271

258272
// LoopInQuote contains estimates for the fees making up the total swap cost

loopd/swapclient_server.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -483,21 +483,26 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
483483
return nil, err
484484
}
485485

486-
var lastHop *route.Vertex
486+
var (
487+
routeHints [][]zpay32.HopHint
488+
lastHop *route.Vertex
489+
)
490+
487491
if req.LoopInLastHop != nil {
488492
lastHopVertex, err := route.NewVertexFromBytes(
489493
req.LoopInLastHop,
490494
)
491495
if err != nil {
492496
return nil, err
493497
}
494-
495498
lastHop = &lastHopVertex
496499
}
497500

498-
routeHints, err := unmarshallRouteHints(req.LoopInRouteHints)
499-
if err != nil {
500-
return nil, err
501+
if len(req.LoopInRouteHints) != 0 {
502+
routeHints, err = unmarshallRouteHints(req.LoopInRouteHints)
503+
if err != nil {
504+
return nil, err
505+
}
501506
}
502507

503508
quote, err := s.impl.LoopInQuote(ctx, &loop.LoopInQuoteRequest{
@@ -506,10 +511,12 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
506511
ExternalHtlc: req.ExternalHtlc,
507512
LastHop: lastHop,
508513
RouteHints: routeHints,
514+
Private: req.Private,
509515
})
510516
if err != nil {
511517
return nil, err
512518
}
519+
513520
return &looprpc.InQuoteResponse{
514521
HtlcPublishFeeSat: int64(quote.MinerFee),
515522
SwapFeeSat: int64(quote.SwapFee),
@@ -613,6 +620,11 @@ func (s *swapClientServer) LoopIn(ctx context.Context,
613620
return nil, err
614621
}
615622

623+
routeHints, err := unmarshallRouteHints(in.RouteHints)
624+
if err != nil {
625+
return nil, err
626+
}
627+
616628
req := &loop.LoopInRequest{
617629
Amount: btcutil.Amount(in.Amt),
618630
MaxMinerFee: btcutil.Amount(in.MaxMinerFee),
@@ -621,6 +633,8 @@ func (s *swapClientServer) LoopIn(ctx context.Context,
621633
ExternalHtlc: in.ExternalHtlc,
622634
Label: in.Label,
623635
Initiator: in.Initiator,
636+
Private: in.Private,
637+
RouteHints: routeHints,
624638
}
625639
if in.LastHop != nil {
626640
lastHop, err := route.NewVertexFromBytes(in.LastHop)

loopin.go

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/lightningnetwork/lnd/lntypes"
2424
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
2525
"github.com/lightningnetwork/lnd/lnwire"
26+
"github.com/lightningnetwork/lnd/routing/route"
2627
)
2728

2829
var (
@@ -80,6 +81,33 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
8081
currentHeight int32, request *LoopInRequest) (*loopInInitResult,
8182
error) {
8283

84+
var err error
85+
86+
// Private and routehints are mutually exclusive as setting private
87+
// means we retrieve our own routehints from the connected node.
88+
if len(request.RouteHints) != 0 && request.Private {
89+
return nil, fmt.Errorf("private and route_hints both set")
90+
}
91+
92+
// If Private is set, we generate route hints
93+
if request.Private {
94+
// If last_hop is set, we'll only add channels with peers
95+
// set to the last_hop parameter
96+
includeNodes := make(map[route.Vertex]struct{})
97+
if request.LastHop != nil {
98+
includeNodes[*request.LastHop] = struct{}{}
99+
}
100+
101+
// Because the Private flag is set, we'll generate our own
102+
// set of hop hints
103+
request.RouteHints, err = SelectHopHints(
104+
globalCtx, cfg.lnd, request.Amount, DefaultMaxHopHints, includeNodes,
105+
)
106+
if err != nil {
107+
return nil, err
108+
}
109+
}
110+
83111
// Request current server loop in terms and use these to calculate the
84112
// swap fee that we should subtract from the swap amount in the payment
85113
// request that we send to the server. We pass nil as optional route
@@ -89,7 +117,7 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
89117
// route hints.
90118
quote, err := cfg.server.GetLoopInQuote(
91119
globalCtx, request.Amount, cfg.lnd.NodePubkey, request.LastHop,
92-
nil,
120+
request.RouteHints,
93121
)
94122
if err != nil {
95123
return nil, wrapGrpcError("loop in terms", err)
@@ -129,10 +157,11 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
129157
// Create the swap invoice in lnd.
130158
_, swapInvoice, err := cfg.lnd.Client.AddInvoice(
131159
globalCtx, &invoicesrpc.AddInvoiceData{
132-
Preimage: &swapPreimage,
133-
Value: lnwire.NewMSatFromSatoshis(swapInvoiceAmt),
134-
Memo: "swap",
135-
Expiry: 3600 * 24 * 365,
160+
Preimage: &swapPreimage,
161+
Value: lnwire.NewMSatFromSatoshis(swapInvoiceAmt),
162+
Memo: "swap",
163+
Expiry: 3600 * 24 * 365,
164+
RouteHints: request.RouteHints,
136165
},
137166
)
138167
if err != nil {
@@ -148,10 +177,11 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
148177
log.Infof("Creating probe invoice %v", probeHash)
149178
probeInvoice, err := cfg.lnd.Invoices.AddHoldInvoice(
150179
globalCtx, &invoicesrpc.AddInvoiceData{
151-
Hash: &probeHash,
152-
Value: lnwire.NewMSatFromSatoshis(swapInvoiceAmt),
153-
Memo: "loop in probe",
154-
Expiry: 3600,
180+
Hash: &probeHash,
181+
Value: lnwire.NewMSatFromSatoshis(swapInvoiceAmt),
182+
Memo: "loop in probe",
183+
Expiry: 3600,
184+
RouteHints: request.RouteHints,
155185
},
156186
)
157187
if err != nil {

0 commit comments

Comments
 (0)