Skip to content

Commit 83628d7

Browse files
authored
Merge pull request #1016 from hieblmi/easy-exclude-peers
easyautoloop: exclude peers
2 parents 62419ab + 70c9513 commit 83628d7

File tree

9 files changed

+935
-690
lines changed

9 files changed

+935
-690
lines changed

cmd/loop/liquidity.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"strconv"
9+
"strings"
910

1011
"github.com/lightninglabs/loop/liquidity"
1112
"github.com/lightninglabs/loop/looprpc"
@@ -350,6 +351,18 @@ var setParamsCommand = &cli.Command{
350351
Usage: "the target size of total local balance in " +
351352
"satoshis, used by easy autoloop.",
352353
},
354+
&cli.StringSliceFlag{
355+
Name: "easyautoloop_excludepeer",
356+
Usage: "list of peer pubkeys (hex) to exclude from " +
357+
"easy autoloop channel selection; repeat " +
358+
"--easyautoloop_excludepeer for multiple peers",
359+
},
360+
&cli.BoolFlag{
361+
Name: "easyatutoloop_includeallpeers",
362+
Usage: "include all peers back into easy autoloop by " +
363+
"clearing the exclusion list. It cannot be " +
364+
"combined with --easyautoloop_excludepeer",
365+
},
353366
&cli.BoolFlag{
354367
Name: "asset_easyautoloop",
355368
Usage: "set to true to enable asset easy autoloop, which " +
@@ -567,6 +580,39 @@ func setParams(ctx context.Context, cmd *cli.Command) error {
567580
flagSet = true
568581
}
569582

583+
// If easyatutoloop_includeallpeers is set, clear the entire exclusion
584+
// list.
585+
if cmd.IsSet("easyatutoloop_includeallpeers") {
586+
if cmd.IsSet("easyautoloop_excludepeer") {
587+
return fmt.Errorf("easyatutoloop_includeallpeers " +
588+
"cannot be used with " +
589+
"--easyautoloop_excludepeer")
590+
}
591+
params.EasyAutoloopExcludedPeers = nil
592+
flagSet = true
593+
}
594+
595+
if cmd.IsSet("easyautoloop_excludepeer") {
596+
peers := cmd.StringSlice("easyautoloop_excludepeer")
597+
// Reset and set according to a provided list.
598+
params.EasyAutoloopExcludedPeers = make([][]byte, 0, len(peers))
599+
for _, s := range peers {
600+
s = strings.TrimSpace(s)
601+
if s == "" {
602+
continue
603+
}
604+
v, err := route.NewVertexFromStr(s)
605+
if err != nil {
606+
return fmt.Errorf("invalid peer pubkey "+
607+
"%s: %v", s, err)
608+
}
609+
params.EasyAutoloopExcludedPeers = append(
610+
params.EasyAutoloopExcludedPeers, v[:],
611+
)
612+
}
613+
flagSet = true
614+
}
615+
570616
if cmd.IsSet("asset_easyautoloop") {
571617
if !cmd.IsSet("asset_id") {
572618
return fmt.Errorf("asset_id must be set to use " +

docs/loop.1

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,15 @@ update the parameters set for the liquidity manager
332332
.PP
333333
\fB--destaddr\fP="": custom address to be used as destination for autoloop loop out, set to "default" in order to revert to default behavior.
334334

335+
.PP
336+
\fB--easyatutoloop_includeallpeers\fP: include all peers back into easy autoloop by clearing the exclusion list. It cannot be combined with --easyautoloop_excludepeer
337+
335338
.PP
336339
\fB--easyautoloop\fP: set to true to enable easy autoloop, which will automatically dispatch swaps in order to meet the target local balance.
337340

341+
.PP
342+
\fB--easyautoloop_excludepeer\fP="": list of peer pubkeys (hex) to exclude from easy autoloop channel selection; repeat --easyautoloop_excludepeer for multiple peers (default: [])
343+
338344
.PP
339345
\fB--failurebackoff\fP="": the amount of time, in seconds, that should pass before a channel that previously had a failed swap will be included in suggestions. (default: 0)
340346

docs/loop.md

Lines changed: 30 additions & 28 deletions
Large diffs are not rendered by default.
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package liquidity
2+
3+
import (
4+
"testing"
5+
6+
"github.com/btcsuite/btcd/btcutil"
7+
"github.com/lightninglabs/lndclient"
8+
"github.com/lightningnetwork/lnd/lnwire"
9+
"github.com/lightningnetwork/lnd/routing/route"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
// TestEasyAutoloopExcludedPeers ensures that peers listed in
14+
// Parameters.EasyAutoloopExcludedPeers are not selected by
15+
// pickEasyAutoloopChannel even if they would otherwise be preferred.
16+
func TestEasyAutoloopExcludedPeers(t *testing.T) {
17+
// Two channels, peer1 has the higher local balance and would be picked
18+
// if not excluded.
19+
ch1 := lndclient.ChannelInfo{
20+
Active: true,
21+
ChannelID: lnwire.NewShortChanIDFromInt(11).ToUint64(),
22+
PubKeyBytes: peer1,
23+
LocalBalance: 90000,
24+
RemoteBalance: 0,
25+
Capacity: 100000,
26+
}
27+
ch2 := lndclient.ChannelInfo{
28+
Active: true,
29+
ChannelID: lnwire.NewShortChanIDFromInt(22).ToUint64(),
30+
PubKeyBytes: peer2,
31+
LocalBalance: 80000,
32+
RemoteBalance: 0,
33+
Capacity: 100000,
34+
}
35+
36+
params := defaultParameters
37+
params.Autoloop = true
38+
params.EasyAutoloop = true
39+
params.EasyAutoloopTarget = 80000
40+
params.ClientRestrictions.Minimum = btcutil.Amount(1)
41+
params.ClientRestrictions.Maximum = btcutil.Amount(10000)
42+
// Exclude peer1, even though its channel has more local balance.
43+
params.EasyAutoloopExcludedPeers = []route.Vertex{peer1}
44+
45+
c := newAutoloopTestCtx(
46+
t, params, []lndclient.ChannelInfo{ch1, ch2}, testRestrictions,
47+
)
48+
49+
// Picking a channel should not pick the excluded peer's channel.
50+
picked := c.manager.pickEasyAutoloopChannel(
51+
[]lndclient.ChannelInfo{ch1, ch2}, &params.ClientRestrictions,
52+
nil, nil, 1,
53+
)
54+
require.NotNil(t, picked)
55+
require.Equal(
56+
t, ch2.ChannelID, picked.ChannelID,
57+
"should pick non-excluded peer's channel",
58+
)
59+
}
60+
61+
// TestEasyAutoloopIncludeAllPeers simulates the --includealleasypeers flag by
62+
// clearing the exclusion list and ensuring a previously excluded peer can be
63+
// selected again.
64+
func TestEasyAutoloopIncludeAllPeers(t *testing.T) {
65+
ch1 := lndclient.ChannelInfo{
66+
Active: true,
67+
ChannelID: lnwire.NewShortChanIDFromInt(33).ToUint64(),
68+
PubKeyBytes: peer1,
69+
LocalBalance: 90000,
70+
RemoteBalance: 0,
71+
Capacity: 100000,
72+
}
73+
ch2 := lndclient.ChannelInfo{
74+
Active: true,
75+
ChannelID: lnwire.NewShortChanIDFromInt(44).ToUint64(),
76+
PubKeyBytes: peer2,
77+
LocalBalance: 80000,
78+
RemoteBalance: 0,
79+
Capacity: 100000,
80+
}
81+
82+
params := defaultParameters
83+
params.Autoloop = true
84+
params.EasyAutoloop = true
85+
params.EasyAutoloopTarget = 80000
86+
params.ClientRestrictions.Minimum = btcutil.Amount(1)
87+
params.ClientRestrictions.Maximum = btcutil.Amount(10000)
88+
params.EasyAutoloopExcludedPeers = []route.Vertex{peer1}
89+
90+
c := newAutoloopTestCtx(
91+
t, params, []lndclient.ChannelInfo{ch1, ch2}, testRestrictions,
92+
)
93+
94+
// With exclusion active, peer1 should not be picked.
95+
picked := c.manager.pickEasyAutoloopChannel(
96+
[]lndclient.ChannelInfo{ch1, ch2}, &params.ClientRestrictions,
97+
nil, nil, 1,
98+
)
99+
require.NotNil(t, picked)
100+
require.Equal(t, ch2.ChannelID, picked.ChannelID)
101+
102+
// Simulate --includealleasypeers by clearing the exclusion list as the
103+
// CLI does before sending to the server.
104+
c.manager.params.EasyAutoloopExcludedPeers = nil
105+
106+
picked = c.manager.pickEasyAutoloopChannel(
107+
[]lndclient.ChannelInfo{ch1, ch2}, &params.ClientRestrictions,
108+
nil, nil, 1,
109+
)
110+
require.NotNil(t, picked)
111+
require.Equal(
112+
t, ch1.ChannelID, picked.ChannelID,
113+
"after include-all, highest local balance should win again",
114+
)
115+
}

liquidity/liquidity.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1666,9 +1666,27 @@ func (m *Manager) pickEasyAutoloopChannel(channels []lndclient.ChannelInfo,
16661666
return channels[i].LocalBalance > channels[j].LocalBalance
16671667
})
16681668

1669-
// Check each channel, since channels are already sorted we return the
1669+
// Build a set of excluded peers for a quick lookup.
1670+
excluded := make(
1671+
map[route.Vertex]struct{},
1672+
len(m.params.EasyAutoloopExcludedPeers),
1673+
)
1674+
for _, v := range m.params.EasyAutoloopExcludedPeers {
1675+
excluded[v] = struct{}{}
1676+
}
1677+
1678+
// Check each channel, since channels are already sorted, we return the
16701679
// first channel that passes all checks.
16711680
for _, channel := range channels {
1681+
// Skip channels whose remote peer is excluded for easy autoloop.
1682+
if _, ok := excluded[channel.PubKeyBytes]; ok {
1683+
log.Debugf("Channel %v cannot be used for easy "+
1684+
"autoloop: peer %v manually excluded",
1685+
channel.ChannelID, channel.PubKeyBytes)
1686+
1687+
continue
1688+
}
1689+
16721690
shortChanID := lnwire.NewShortChanIDFromInt(channel.ChannelID)
16731691

16741692
if !channel.Active {

liquidity/parameters.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ type Parameters struct {
116116
// maintain in our channels.
117117
EasyAutoloopTarget btcutil.Amount
118118

119+
// EasyAutoloopExcludedPeers is an optional list of peers that should be
120+
// excluded from being selected for easy autoloop swaps.
121+
EasyAutoloopExcludedPeers []route.Vertex
122+
119123
// AssetAutoloopParams maps an asset id hex encoded string to its
120124
// easy autoloop parameters.
121125
AssetAutoloopParams map[string]AssetParams
@@ -481,6 +485,21 @@ func RpcToParameters(req *clientrpc.LiquidityParameters) (*Parameters,
481485
time.Second
482486
}
483487

488+
// Map excluded peers for easy autoloop, if any.
489+
excludedPeersRPC := req.GetEasyAutoloopExcludedPeers()
490+
params.EasyAutoloopExcludedPeers = make(
491+
[]route.Vertex, 0, len(excludedPeersRPC),
492+
)
493+
for _, p := range excludedPeersRPC {
494+
v, err := route.NewVertexFromBytes(p)
495+
if err != nil {
496+
return nil, err
497+
}
498+
params.EasyAutoloopExcludedPeers = append(
499+
params.EasyAutoloopExcludedPeers, v,
500+
)
501+
}
502+
484503
// If an old-style budget was written to storage then express it by
485504
// using the new auto budget parameters. If the newly added parameters
486505
// have the 0 default value, but a budget was defined that means the
@@ -603,6 +622,15 @@ func ParametersToRpc(cfg Parameters) (*clientrpc.LiquidityParameters,
603622
EasyAssetParams: easyAssetMap,
604623
FastSwapPublication: cfg.FastSwapPublication,
605624
}
625+
// Set excluded peers for easy autoloop.
626+
rpcCfg.EasyAutoloopExcludedPeers = make(
627+
[][]byte, 0, len(cfg.EasyAutoloopExcludedPeers),
628+
)
629+
for _, v := range cfg.EasyAutoloopExcludedPeers {
630+
rpcCfg.EasyAutoloopExcludedPeers = append(
631+
rpcCfg.EasyAutoloopExcludedPeers, v[:],
632+
)
633+
}
606634

607635
switch f := cfg.FeeLimit.(type) {
608636
case *FeeCategoryLimit:

0 commit comments

Comments
 (0)