Skip to content

Commit f6d49f9

Browse files
committed
all loopin quote changes
1 parent b54b894 commit f6d49f9

13 files changed

+1161
-962
lines changed

client.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ func (s *Client) LoopInQuote(ctx context.Context,
684684

685685
quote, err := s.Server.GetLoopInQuote(
686686
ctx, request.Amount, s.lndServices.NodePubkey, request.LastHop,
687-
request.RouteHints, request.Initiator,
687+
request.RouteHints, request.Initiator, request.NumDeposits,
688688
)
689689
if err != nil {
690690
return nil, err
@@ -701,6 +701,15 @@ func (s *Client) LoopInQuote(ctx context.Context,
701701
}, nil
702702
}
703703

704+
// We don't calculate the on-chain fee if the loop in is funded by
705+
// static address deposits.
706+
if request.NumDeposits > 0 {
707+
return &LoopInQuote{
708+
SwapFee: swapFee,
709+
MinerFee: 0,
710+
}, nil
711+
}
712+
704713
// Get estimate for miner fee. If estimating the miner fee for the
705714
// requested amount is not possible because lnd's wallet cannot
706715
// construct a sample TX, we just return zero instead of failing the

cmd/loop/staticaddr.go

+138
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010

1111
"github.com/btcsuite/btcd/chaincfg/chainhash"
1212
"github.com/lightninglabs/loop/looprpc"
13+
"github.com/lightninglabs/loop/swapserverrpc"
14+
"github.com/lightningnetwork/lnd/routing/route"
1315
"github.com/urfave/cli"
1416
)
1517

@@ -23,6 +25,7 @@ var staticAddressCommands = cli.Command{
2325
listUnspentCommand,
2426
withdrawalCommand,
2527
summaryCommand,
28+
staticQuoteCommand,
2629
},
2730
}
2831

@@ -297,3 +300,138 @@ func NewProtoOutPoint(op string) (*looprpc.OutPoint, error) {
297300
OutputIndex: uint32(outputIndex),
298301
}, nil
299302
}
303+
304+
var staticQuoteCommand = cli.Command{
305+
Name: "quote",
306+
ShortName: "q",
307+
Usage: "quote...",
308+
Description: `
309+
Allows to determine the cost of a swap up front.
310+
`,
311+
Flags: []cli.Flag{
312+
cli.StringSliceFlag{
313+
Name: "utxo",
314+
Usage: "specify utxos as outpoints(tx:idx) which will" +
315+
"be quoted for.",
316+
},
317+
cli.BoolFlag{
318+
Name: "all",
319+
Usage: "quotes for all static address deposits.",
320+
},
321+
cli.StringFlag{
322+
Name: lastHopFlag.Name,
323+
Usage: "the pubkey of the last hop to use for the " +
324+
"quote",
325+
},
326+
privateFlag,
327+
routeHintsFlag,
328+
},
329+
Action: staticAddressQuote,
330+
}
331+
332+
func staticAddressQuote(ctx *cli.Context) error {
333+
if ctx.NArg() > 0 {
334+
return cli.ShowCommandHelp(ctx, "new")
335+
}
336+
337+
client, cleanup, err := getClient(ctx)
338+
if err != nil {
339+
return err
340+
}
341+
defer cleanup()
342+
343+
var (
344+
ctxb = context.Background()
345+
isAllSelected = ctx.IsSet("all")
346+
isUtxoSelected = ctx.IsSet("utxo")
347+
hints []*swapserverrpc.RouteHint
348+
lastHop []byte
349+
)
350+
351+
// Private and route hints are mutually exclusive as setting private
352+
// means we retrieve our own route hints from the connected node.
353+
hints, err = validateRouteHints(ctx)
354+
if err != nil {
355+
return err
356+
}
357+
358+
if ctx.IsSet(lastHopFlag.Name) {
359+
lastHopVertex, err := route.NewVertexFromStr(
360+
ctx.String(lastHopFlag.Name),
361+
)
362+
if err != nil {
363+
return err
364+
}
365+
366+
lastHop = lastHopVertex[:]
367+
}
368+
369+
summaryResp, err := client.GetStaticAddressSummary(
370+
ctxb, &looprpc.StaticAddressSummaryRequest{},
371+
)
372+
if err != nil {
373+
return err
374+
}
375+
376+
var quoteAmount int64
377+
var numDeposits uint32
378+
switch {
379+
case isAllSelected == isUtxoSelected:
380+
return errors.New("must select either all or some utxos")
381+
382+
case isAllSelected:
383+
numDeposits = summaryResp.TotalNumDeposits
384+
quoteAmount = summaryResp.ValueDeposited
385+
386+
case isUtxoSelected:
387+
utxos := ctx.StringSlice("utxo")
388+
numDeposits = uint32(len(utxos))
389+
quoteAmount, err = sumOutpointValues(
390+
utxos, summaryResp.FilteredDeposits,
391+
)
392+
if err != nil {
393+
return err
394+
}
395+
396+
default:
397+
return fmt.Errorf("unknown quote request")
398+
}
399+
400+
resp, err := client.GetLoopInQuote(
401+
ctxb, &looprpc.QuoteRequest{
402+
Amt: quoteAmount,
403+
LoopInRouteHints: hints,
404+
LoopInLastHop: lastHop,
405+
Private: ctx.Bool(privateFlag.Name),
406+
NumDeposits: numDeposits,
407+
},
408+
)
409+
if err != nil {
410+
return err
411+
}
412+
413+
printRespJSON(resp)
414+
415+
return nil
416+
}
417+
418+
func sumOutpointValues(utxos []string, deposits []*looprpc.Deposit) (int64,
419+
error) {
420+
421+
var sum int64
422+
for _, utxo := range utxos {
423+
found := false
424+
for _, deposit := range deposits {
425+
if deposit.Outpoint == utxo {
426+
sum += deposit.Value
427+
found = true
428+
}
429+
}
430+
if !found {
431+
return 0, fmt.Errorf("utxo %v not found in deposits",
432+
utxo)
433+
}
434+
}
435+
436+
return sum, nil
437+
}

interface.go

+2
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ type LoopInQuoteRequest struct {
282282
// initiated the swap (loop CLI, autolooper, LiT UI and so on) and is
283283
// appended to the user agent string.
284284
Initiator string
285+
286+
NumDeposits uint32
285287
}
286288

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

loopd/swapclient_server.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,7 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
737737
log.Infof("Loop in quote request received")
738738

739739
htlcConfTarget, err := validateLoopInRequest(
740-
req.ConfTarget, req.ExternalHtlc,
740+
req.ConfTarget, req.ExternalHtlc, req.NumDeposits,
741741
)
742742
if err != nil {
743743
return nil, err
@@ -773,6 +773,7 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
773773
RouteHints: routeHints,
774774
Private: req.Private,
775775
Initiator: defaultLoopdInitiator,
776+
NumDeposits: req.NumDeposits,
776777
})
777778
if err != nil {
778779
return nil, err
@@ -869,7 +870,7 @@ func (s *swapClientServer) LoopIn(ctx context.Context,
869870
log.Infof("Loop in request received")
870871

871872
htlcConfTarget, err := validateLoopInRequest(
872-
in.HtlcConfTarget, in.ExternalHtlc,
873+
in.HtlcConfTarget, in.ExternalHtlc, 0,
873874
)
874875
if err != nil {
875876
return nil, err
@@ -1657,7 +1658,9 @@ func validateConfTarget(target, defaultTarget int32) (int32, error) {
16571658

16581659
// validateLoopInRequest fails if the mutually exclusive conf target and
16591660
// external parameters are both set.
1660-
func validateLoopInRequest(htlcConfTarget int32, external bool) (int32, error) {
1661+
func validateLoopInRequest(htlcConfTarget int32, external bool,
1662+
numDeposits uint32) (int32, error) {
1663+
16611664
// If the htlc is going to be externally set, the htlcConfTarget should
16621665
// not be set, because it has no relevance when the htlc is external.
16631666
if external && htlcConfTarget != 0 {
@@ -1671,6 +1674,12 @@ func validateLoopInRequest(htlcConfTarget int32, external bool) (int32, error) {
16711674
return 0, nil
16721675
}
16731676

1677+
// If the loop in uses static address deposits, we do not need to set a
1678+
// confirmation target since the HTLC won't be published by the client.
1679+
if numDeposits > 0 {
1680+
return 0, nil
1681+
}
1682+
16741683
return validateConfTarget(htlcConfTarget, loop.DefaultHtlcConfTarget)
16751684
}
16761685

loopd/swapclient_server_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ func TestValidateLoopInRequest(t *testing.T) {
193193
t.Run(test.name, func(t *testing.T) {
194194
external := test.external
195195
conf, err := validateLoopInRequest(
196-
test.confTarget, external,
196+
test.confTarget, external, 0,
197197
)
198198

199199
if test.expectErr {

loopin.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
128128
// hints.
129129
quote, err := cfg.server.GetLoopInQuote(
130130
globalCtx, request.Amount, cfg.lnd.NodePubkey, request.LastHop,
131-
request.RouteHints, request.Initiator,
131+
request.RouteHints, request.Initiator, 0,
132132
)
133133
if err != nil {
134134
return nil, wrapGrpcError("loop in terms", err)

0 commit comments

Comments
 (0)