Skip to content

Commit bbe7c69

Browse files
authored
Merge pull request #542 from GeorgeTsagk/autoloop-custom-addr
liquidity: add `destaddr` parameter for autoloop loop out
2 parents 44ba94e + e1ecc27 commit bbe7c69

10 files changed

+390
-170
lines changed

cmd/loop/liquidity.go

+11
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,12 @@ var setParamsCommand = cli.Command{
286286
"of swaps, limited to the budget set by " +
287287
"autobudget",
288288
},
289+
cli.StringFlag{
290+
Name: "destaddr",
291+
Usage: "custom address to be used as destination for " +
292+
"autoloop loop out, set to \"default\" in " +
293+
"order to revert to default behavior",
294+
},
289295
cli.Uint64Flag{
290296
Name: "autobudget",
291297
Usage: "the maximum amount of fees in satoshis that " +
@@ -429,6 +435,11 @@ func setParams(ctx *cli.Context) error {
429435
flagSet = true
430436
}
431437

438+
if ctx.IsSet("destaddr") {
439+
params.AutoloopDestAddress = ctx.String("destaddr")
440+
flagSet = true
441+
}
442+
432443
if ctx.IsSet("budgetstart") {
433444
params.AutoloopBudgetStartSec = ctx.Uint64("budgetstart")
434445
flagSet = true

liquidity/autoloop_test.go

+151
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/lightningnetwork/lnd/lntypes"
1515
"github.com/lightningnetwork/lnd/lnwire"
1616
"github.com/lightningnetwork/lnd/routing/route"
17+
"github.com/stretchr/testify/require"
1718
)
1819

1920
// TestAutoLoopDisabled tests the case where we need to perform a swap, but
@@ -307,6 +308,156 @@ func TestAutoLoopEnabled(t *testing.T) {
307308
c.stop()
308309
}
309310

311+
// TestAutoloopAddress tests that the custom destination address feature for
312+
// loop out behaves as expected.
313+
func TestAutoloopAddress(t *testing.T) {
314+
defer test.Guard(t)()
315+
316+
// Decode a dummy p2wkh address to use as the destination address for
317+
// the swaps.
318+
p2wkhAddr := "bcrt1qq68r6ff4k4pjx39efs44gcyccf7unqnu5qtjjz"
319+
addr, err := btcutil.DecodeAddress(p2wkhAddr, nil)
320+
if err != nil {
321+
t.Error(err)
322+
}
323+
324+
var (
325+
channels = []lndclient.ChannelInfo{
326+
channel1, channel2,
327+
}
328+
329+
swapFeePPM uint64 = 1000
330+
routeFeePPM uint64 = 1000
331+
prepayFeePPM uint64 = 1000
332+
prepayAmount = btcutil.Amount(20000)
333+
maxMiner = btcutil.Amount(20000)
334+
335+
// Create some dummy parameters for autoloop and also specify an
336+
// destination address.
337+
params = Parameters{
338+
Autoloop: true,
339+
AutoFeeBudget: 40066,
340+
DestAddr: addr,
341+
AutoFeeStartDate: testTime,
342+
MaxAutoInFlight: 2,
343+
FailureBackOff: time.Hour,
344+
SweepConfTarget: 10,
345+
FeeLimit: NewFeeCategoryLimit(
346+
swapFeePPM, routeFeePPM, prepayFeePPM, maxMiner,
347+
prepayAmount, 20000,
348+
),
349+
ChannelRules: map[lnwire.ShortChannelID]*SwapRule{
350+
chanID1: chanRule,
351+
chanID2: chanRule,
352+
},
353+
HtlcConfTarget: defaultHtlcConfTarget,
354+
}
355+
)
356+
c := newAutoloopTestCtx(t, params, channels, testRestrictions)
357+
c.start()
358+
359+
// Get parameters from manager and verify that address is set correctly.
360+
params = c.manager.GetParameters()
361+
require.Equal(t, params.DestAddr, addr)
362+
363+
// Calculate our maximum allowed fees and create quotes that fall within
364+
// our budget.
365+
var (
366+
amt = chan1Rec.Amount
367+
368+
maxSwapFee = ppmToSat(amt, swapFeePPM)
369+
370+
quote1 = &loop.LoopOutQuote{
371+
SwapFee: maxSwapFee,
372+
PrepayAmount: prepayAmount - 10,
373+
MinerFee: maxMiner - 10,
374+
}
375+
376+
quote2 = &loop.LoopOutQuote{
377+
SwapFee: maxSwapFee,
378+
PrepayAmount: prepayAmount - 20,
379+
MinerFee: maxMiner - 10,
380+
}
381+
382+
quoteRequest = &loop.LoopOutQuoteRequest{
383+
Amount: amt,
384+
SweepConfTarget: params.SweepConfTarget,
385+
}
386+
387+
quotes = []quoteRequestResp{
388+
{
389+
request: quoteRequest,
390+
quote: quote1,
391+
},
392+
{
393+
request: quoteRequest,
394+
quote: quote2,
395+
},
396+
}
397+
398+
maxRouteFee = ppmToSat(amt, routeFeePPM)
399+
400+
chan1Swap = &loop.OutRequest{
401+
Amount: amt,
402+
// Define the expected destination address.
403+
DestAddr: addr,
404+
MaxSwapRoutingFee: maxRouteFee,
405+
MaxPrepayRoutingFee: ppmToSat(
406+
quote1.PrepayAmount, prepayFeePPM,
407+
),
408+
MaxSwapFee: quote1.SwapFee,
409+
MaxPrepayAmount: quote1.PrepayAmount,
410+
MaxMinerFee: maxMiner,
411+
SweepConfTarget: params.SweepConfTarget,
412+
OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()},
413+
Label: labels.AutoloopLabel(swap.TypeOut),
414+
Initiator: autoloopSwapInitiator,
415+
}
416+
417+
chan2Swap = &loop.OutRequest{
418+
Amount: amt,
419+
// Define the expected destination address.
420+
DestAddr: addr,
421+
MaxSwapRoutingFee: maxRouteFee,
422+
MaxPrepayRoutingFee: ppmToSat(
423+
quote2.PrepayAmount, routeFeePPM,
424+
),
425+
MaxSwapFee: quote2.SwapFee,
426+
MaxPrepayAmount: quote2.PrepayAmount,
427+
MaxMinerFee: maxMiner,
428+
SweepConfTarget: params.SweepConfTarget,
429+
OutgoingChanSet: loopdb.ChannelSet{chanID2.ToUint64()},
430+
Label: labels.AutoloopLabel(swap.TypeOut),
431+
Initiator: autoloopSwapInitiator,
432+
}
433+
434+
loopOuts = []loopOutRequestResp{
435+
{
436+
request: chan1Swap,
437+
response: &loop.LoopOutSwapInfo{
438+
SwapHash: lntypes.Hash{1},
439+
},
440+
},
441+
{
442+
request: chan2Swap,
443+
response: &loop.LoopOutSwapInfo{
444+
SwapHash: lntypes.Hash{2},
445+
},
446+
},
447+
}
448+
)
449+
450+
step := &autoloopStep{
451+
minAmt: 1,
452+
maxAmt: amt + 1,
453+
quotesOut: quotes,
454+
expectedOut: loopOuts,
455+
}
456+
c.autoloop(step)
457+
458+
c.stop()
459+
}
460+
310461
// TestCompositeRules tests the case where we have rules set on a per peer
311462
// and per channel basis, and perform swaps for both targets.
312463
func TestCompositeRules(t *testing.T) {

liquidity/autoloop_testcontext_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,9 @@ func (c *autoloopTestCtx) autoloop(step *autoloopStep) {
304304

305305
// Set our destination address to nil so that we do not need to
306306
// provide the address that is obtained by the mock wallet kit.
307-
actual.DestAddr = nil
307+
if expected.request.DestAddr == nil {
308+
actual.DestAddr = nil
309+
}
308310

309311
assert.Equal(c.t, expected.request, actual)
310312
c.loopOut <- expected.response

liquidity/liquidity.go

+7
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,13 @@ func (m *Manager) autoloop(ctx context.Context) error {
380380

381381
// Create a copy of our range var so that we can reference it.
382382
swap := swap
383+
384+
// Check if the parameter for custom address is defined for loop
385+
// outs.
386+
if m.params.DestAddr != nil {
387+
swap.DestAddr = m.params.DestAddr
388+
}
389+
383390
loopOut, err := m.cfg.LoopOut(ctx, &swap)
384391
if err != nil {
385392
return err

liquidity/parameters.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ import (
1818

1919
var (
2020
// defaultParameters contains the default parameters that we start our
21-
// liquidity manger with.
21+
// liquidity manager with.
2222
defaultParameters = Parameters{
2323
AutoFeeBudget: defaultBudget,
24+
DestAddr: nil,
2425
MaxAutoInFlight: defaultMaxInFlight,
2526
ChannelRules: make(map[lnwire.ShortChannelID]*SwapRule),
2627
PeerRules: make(map[route.Vertex]*SwapRule),
@@ -37,6 +38,10 @@ type Parameters struct {
3738
// Autoloop enables automatic dispatch of swaps.
3839
Autoloop bool
3940

41+
// DestAddr is the address to be used for sweeping the on-chain HTLC that
42+
// is related with a loop out.
43+
DestAddr btcutil.Address
44+
4045
// AutoFeeBudget is the total amount we allow to be spent on
4146
// automatically dispatched swaps. Once this budget has been used, we
4247
// will stop dispatching swaps until the budget is increased or the
@@ -347,12 +352,25 @@ func rpcToParameters(req *clientrpc.LiquidityParameters) (*Parameters,
347352
return nil, err
348353
}
349354

355+
var destaddr btcutil.Address
356+
if len(req.AutoloopDestAddress) != 0 {
357+
if req.AutoloopDestAddress == "default" {
358+
destaddr = nil
359+
} else {
360+
destaddr, err = btcutil.DecodeAddress(req.AutoloopDestAddress, nil)
361+
if err != nil {
362+
return nil, err
363+
}
364+
}
365+
}
366+
350367
params := &Parameters{
351368
FeeLimit: feeLimit,
352369
SweepConfTarget: req.SweepConfTarget,
353370
FailureBackOff: time.Duration(req.FailureBackoffSec) *
354371
time.Second,
355372
Autoloop: req.Autoloop,
373+
DestAddr: destaddr,
356374
AutoFeeBudget: btcutil.Amount(req.AutoloopBudgetSat),
357375
MaxAutoInFlight: int(req.AutoMaxInFlight),
358376
ChannelRules: make(

loopd/swapclient_server.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -747,12 +747,18 @@ func (s *swapClientServer) GetLiquidityParams(_ context.Context,
747747

748748
totalRules := len(cfg.ChannelRules) + len(cfg.PeerRules)
749749

750+
var destaddr string
751+
if cfg.DestAddr != nil {
752+
destaddr = cfg.DestAddr.String()
753+
}
754+
750755
rpcCfg := &clientrpc.LiquidityParameters{
751-
SweepConfTarget: cfg.SweepConfTarget,
752-
FailureBackoffSec: uint64(cfg.FailureBackOff.Seconds()),
753-
Autoloop: cfg.Autoloop,
754-
AutoloopBudgetSat: uint64(cfg.AutoFeeBudget),
755-
AutoMaxInFlight: uint64(cfg.MaxAutoInFlight),
756+
SweepConfTarget: cfg.SweepConfTarget,
757+
FailureBackoffSec: uint64(cfg.FailureBackOff.Seconds()),
758+
Autoloop: cfg.Autoloop,
759+
AutoloopBudgetSat: uint64(cfg.AutoFeeBudget),
760+
AutoMaxInFlight: uint64(cfg.MaxAutoInFlight),
761+
AutoloopDestAddress: destaddr,
756762
Rules: make(
757763
[]*clientrpc.LiquidityRule, 0, totalRules,
758764
),

0 commit comments

Comments
 (0)