From 25b8d20f7538e2abefb103709cd281a7bf6c8052 Mon Sep 17 00:00:00 2001 From: carla Date: Tue, 30 Nov 2021 13:18:27 +0200 Subject: [PATCH] liquidity: add type to rules In preparation for adding loop in swaps, we relate liquidity rules to a specific type of swap that we want to dispatch. This allows us to use a single rule format for multiple swap types. --- liquidity/autoloop_test.go | 8 +-- liquidity/liquidity.go | 14 +++--- liquidity/liquidity_test.go | 98 +++++++++++++++++++++++++------------ liquidity/threshold_rule.go | 7 +++ loopd/swapclient_server.go | 19 ++++--- 5 files changed, 95 insertions(+), 51 deletions(-) diff --git a/liquidity/autoloop_test.go b/liquidity/autoloop_test.go index 0cb6480..72e0e5a 100644 --- a/liquidity/autoloop_test.go +++ b/liquidity/autoloop_test.go @@ -27,7 +27,7 @@ func TestAutoLoopDisabled(t *testing.T) { } params := defaultParameters - params.ChannelRules = map[lnwire.ShortChannelID]*ThresholdRule{ + params.ChannelRules = map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, } @@ -95,7 +95,7 @@ func TestAutoLoopEnabled(t *testing.T) { swapFeePPM, routeFeePPM, prepayFeePPM, maxMiner, prepayAmount, 20000, ), - ChannelRules: map[lnwire.ShortChannelID]*ThresholdRule{ + ChannelRules: map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, chanID2: chanRule, }, @@ -312,10 +312,10 @@ func TestCompositeRules(t *testing.T) { MaxAutoInFlight: 2, FailureBackOff: time.Hour, SweepConfTarget: 10, - ChannelRules: map[lnwire.ShortChannelID]*ThresholdRule{ + ChannelRules: map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, }, - PeerRules: map[route.Vertex]*ThresholdRule{ + PeerRules: map[route.Vertex]*SwapRule{ peer2: chanRule, }, } diff --git a/liquidity/liquidity.go b/liquidity/liquidity.go index f1bfe0c..e2102c9 100644 --- a/liquidity/liquidity.go +++ b/liquidity/liquidity.go @@ -97,8 +97,8 @@ var ( defaultParameters = Parameters{ AutoFeeBudget: defaultBudget, MaxAutoInFlight: defaultMaxInFlight, - ChannelRules: make(map[lnwire.ShortChannelID]*ThresholdRule), - PeerRules: make(map[route.Vertex]*ThresholdRule), + ChannelRules: make(map[lnwire.ShortChannelID]*SwapRule), + PeerRules: make(map[route.Vertex]*SwapRule), FailureBackOff: defaultFailureBackoff, SweepConfTarget: defaultConfTarget, FeeLimit: defaultFeePortion(), @@ -216,13 +216,13 @@ type Parameters struct { // ChannelRules maps a short channel ID to a rule that describes how we // would like liquidity to be managed. These rules and PeerRules are // exclusively set to prevent overlap between peer and channel rules. - ChannelRules map[lnwire.ShortChannelID]*ThresholdRule + ChannelRules map[lnwire.ShortChannelID]*SwapRule // PeerRules maps a peer's pubkey to a rule that applies to all the // channels that we have with the peer collectively. These rules and // ChannelRules are exclusively set to prevent overlap between peer // and channel rules map to avoid ambiguity. - PeerRules map[route.Vertex]*ThresholdRule + PeerRules map[route.Vertex]*SwapRule } // String returns the string representation of our parameters. @@ -473,7 +473,7 @@ func (m *Manager) SetParameters(ctx context.Context, params Parameters) error { func cloneParameters(params Parameters) Parameters { paramCopy := params paramCopy.ChannelRules = make( - map[lnwire.ShortChannelID]*ThresholdRule, + map[lnwire.ShortChannelID]*SwapRule, len(params.ChannelRules), ) @@ -483,7 +483,7 @@ func cloneParameters(params Parameters) Parameters { } paramCopy.PeerRules = make( - map[route.Vertex]*ThresholdRule, + map[route.Vertex]*SwapRule, len(params.PeerRules), ) @@ -841,7 +841,7 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) ( // suggestSwap checks whether we can currently perform a swap, and creates a // swap request for the rule provided. func (m *Manager) suggestSwap(ctx context.Context, traffic *swapTraffic, - balance *balances, rule *ThresholdRule, restrictions *Restrictions, + balance *balances, rule *SwapRule, restrictions *Restrictions, autoloop bool) (swapSuggestion, error) { // First, check whether this peer/channel combination is already in use diff --git a/liquidity/liquidity_test.go b/liquidity/liquidity_test.go index ccded3a..d2c2f13 100644 --- a/liquidity/liquidity_test.go +++ b/liquidity/liquidity_test.go @@ -47,7 +47,10 @@ var ( } // chanRule is a rule that produces chan1Rec. - chanRule = NewThresholdRule(50, 0) + chanRule = &SwapRule{ + ThresholdRule: NewThresholdRule(50, 0), + Type: swap.TypeOut, + } testQuote = &loop.LoopOutQuote{ SwapFee: btcutil.Amount(5), @@ -188,7 +191,10 @@ func TestParameters(t *testing.T) { require.Equal(t, defaultParameters, startParams) // Mutate the parameters returned by our get function. - startParams.ChannelRules[chanID] = NewThresholdRule(1, 1) + startParams.ChannelRules[chanID] = &SwapRule{ + ThresholdRule: NewThresholdRule(1, 1), + Type: swap.TypeOut, + } // Make sure that we have not mutated the liquidity manager's params // by making this change. @@ -197,9 +203,13 @@ func TestParameters(t *testing.T) { // Provide a valid set of parameters and validate assert that they are // set. - originalRule := NewThresholdRule(10, 10) + originalRule := &SwapRule{ + ThresholdRule: NewThresholdRule(10, 10), + Type: swap.TypeOut, + } + expected := defaultParameters - expected.ChannelRules = map[lnwire.ShortChannelID]*ThresholdRule{ + expected.ChannelRules = map[lnwire.ShortChannelID]*SwapRule{ chanID: originalRule, } @@ -208,15 +218,21 @@ func TestParameters(t *testing.T) { // Check that changing the parameters we just set does not mutate // our liquidity manager's parameters. - expected.ChannelRules[chanID] = NewThresholdRule(11, 11) + expected.ChannelRules[chanID] = &SwapRule{ + ThresholdRule: NewThresholdRule(11, 11), + Type: swap.TypeOut, + } params = manager.GetParameters() require.NoError(t, err) require.Equal(t, originalRule, params.ChannelRules[chanID]) // Set invalid parameters and assert that we fail. - expected.ChannelRules = map[lnwire.ShortChannelID]*ThresholdRule{ - lnwire.NewShortChanIDFromInt(0): NewThresholdRule(1, 2), + expected.ChannelRules = map[lnwire.ShortChannelID]*SwapRule{ + lnwire.NewShortChanIDFromInt(0): { + ThresholdRule: NewThresholdRule(1, 2), + Type: swap.TypeOut, + }, } err = manager.SetParameters(context.Background(), expected) require.Equal(t, ErrZeroChannelID, err) @@ -310,7 +326,7 @@ func TestRestrictedSuggestions(t *testing.T) { ), } - chanRules = map[lnwire.ShortChannelID]*ThresholdRule{ + chanRules = map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, chanID2: chanRule, } @@ -321,8 +337,8 @@ func TestRestrictedSuggestions(t *testing.T) { channels []lndclient.ChannelInfo loopOut []*loopdb.LoopOut loopIn []*loopdb.LoopIn - chanRules map[lnwire.ShortChannelID]*ThresholdRule - peerRules map[route.Vertex]*ThresholdRule + chanRules map[lnwire.ShortChannelID]*SwapRule + peerRules map[route.Vertex]*SwapRule expected *Suggestions }{ { @@ -511,8 +527,11 @@ func TestRestrictedSuggestions(t *testing.T) { Contract: chan1Out, }, }, - peerRules: map[route.Vertex]*ThresholdRule{ - peer1: NewThresholdRule(0, 50), + peerRules: map[route.Vertex]*SwapRule{ + peer1: { + ThresholdRule: NewThresholdRule(0, 50), + Type: swap.TypeOut, + }, }, expected: &Suggestions{ DisqualifiedChans: noneDisqualified, @@ -629,7 +648,7 @@ func TestSweepFeeLimit(t *testing.T) { ppmToSat(7500, defaultPrepayRoutingFeePPM) + ppmToSat(7500, defaultRoutingFeePPM) - params.ChannelRules = map[lnwire.ShortChannelID]*ThresholdRule{ + params.ChannelRules = map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, } @@ -654,21 +673,21 @@ func TestSuggestSwaps(t *testing.T) { tests := []struct { name string channels []lndclient.ChannelInfo - rules map[lnwire.ShortChannelID]*ThresholdRule - peerRules map[route.Vertex]*ThresholdRule + rules map[lnwire.ShortChannelID]*SwapRule + peerRules map[route.Vertex]*SwapRule suggestions *Suggestions err error }{ { name: "no rules", channels: singleChannel, - rules: map[lnwire.ShortChannelID]*ThresholdRule{}, + rules: map[lnwire.ShortChannelID]*SwapRule{}, err: ErrNoRules, }, { name: "loop out", channels: singleChannel, - rules: map[lnwire.ShortChannelID]*ThresholdRule{ + rules: map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, }, suggestions: &Suggestions{ @@ -682,8 +701,11 @@ func TestSuggestSwaps(t *testing.T) { { name: "no rule for channel", channels: singleChannel, - rules: map[lnwire.ShortChannelID]*ThresholdRule{ - chanID2: NewThresholdRule(10, 10), + rules: map[lnwire.ShortChannelID]*SwapRule{ + chanID2: { + ThresholdRule: NewThresholdRule(10, 10), + Type: swap.TypeOut, + }, }, suggestions: &Suggestions{ DisqualifiedChans: noneDisqualified, @@ -715,9 +737,15 @@ func TestSuggestSwaps(t *testing.T) { RemoteBalance: 3000, }, }, - peerRules: map[route.Vertex]*ThresholdRule{ - peer1: NewThresholdRule(80, 0), - peer2: NewThresholdRule(40, 50), + peerRules: map[route.Vertex]*SwapRule{ + peer1: { + ThresholdRule: NewThresholdRule(80, 0), + Type: swap.TypeOut, + }, + peer2: { + ThresholdRule: NewThresholdRule(40, 50), + Type: swap.TypeOut, + }, }, suggestions: &Suggestions{ OutSwaps: []loop.OutRequest{ @@ -869,7 +897,7 @@ func TestFeeLimits(t *testing.T) { ppmToSat(7500, defaultPrepayRoutingFeePPM) + ppmToSat(7500, defaultRoutingFeePPM) - params.ChannelRules = map[lnwire.ShortChannelID]*ThresholdRule{ + params.ChannelRules = map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, } @@ -1061,7 +1089,7 @@ func TestFeeBudget(t *testing.T) { } params := defaultParameters - params.ChannelRules = map[lnwire.ShortChannelID]*ThresholdRule{ + params.ChannelRules = map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, chanID2: chanRule, } @@ -1100,7 +1128,7 @@ func TestInFlightLimit(t *testing.T) { existingSwaps []*loopdb.LoopOut // peerRules will only be set (instead of test default values) // is it is non-nil. - peerRules map[route.Vertex]*ThresholdRule + peerRules map[route.Vertex]*SwapRule suggestions *Suggestions }{ { @@ -1189,9 +1217,15 @@ func TestInFlightLimit(t *testing.T) { // Create two peer-level rules, both in need of a swap, // but peer 1 needs a larger swap so will be // prioritized. - peerRules: map[route.Vertex]*ThresholdRule{ - peer1: NewThresholdRule(50, 0), - peer2: NewThresholdRule(40, 0), + peerRules: map[route.Vertex]*SwapRule{ + peer1: { + ThresholdRule: NewThresholdRule(50, 0), + Type: swap.TypeOut, + }, + peer2: { + ThresholdRule: NewThresholdRule(40, 0), + Type: swap.TypeOut, + }, }, suggestions: &Suggestions{ OutSwaps: []loop.OutRequest{ @@ -1224,7 +1258,7 @@ func TestInFlightLimit(t *testing.T) { params.PeerRules = testCase.peerRules } else { params.ChannelRules = - map[lnwire.ShortChannelID]*ThresholdRule{ + map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, chanID2: chanRule, } @@ -1364,7 +1398,7 @@ func TestSizeRestrictions(t *testing.T) { params := defaultParameters params.ClientRestrictions = testCase.clientRestrictions - params.ChannelRules = map[lnwire.ShortChannelID]*ThresholdRule{ + params.ChannelRules = map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, } @@ -1522,7 +1556,7 @@ func TestFeePercentage(t *testing.T) { params := defaultParameters params.FeeLimit = NewFeePortion(testCase.feePPM) - params.ChannelRules = map[lnwire.ShortChannelID]*ThresholdRule{ + params.ChannelRules = map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, } @@ -1572,7 +1606,7 @@ func testSuggestSwaps(t *testing.T, setup *testSuggestSwapsSetup, } params := defaultParameters - params.ChannelRules = map[lnwire.ShortChannelID]*ThresholdRule{ + params.ChannelRules = map[lnwire.ShortChannelID]*SwapRule{ chanID1: chanRule, chanID2: chanRule, } diff --git a/liquidity/threshold_rule.go b/liquidity/threshold_rule.go index 2db9f6c..8c09fe3 100644 --- a/liquidity/threshold_rule.go +++ b/liquidity/threshold_rule.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/btcsuite/btcutil" + "github.com/lightninglabs/loop/swap" ) var ( @@ -19,6 +20,12 @@ var ( "percentages must be < 100") ) +// SwapRule is a liquidity rule with a specific swap type. +type SwapRule struct { + *ThresholdRule + swap.Type +} + // ThresholdRule is a liquidity rule that implements minimum incoming and // outgoing liquidity threshold. type ThresholdRule struct { diff --git a/loopd/swapclient_server.go b/loopd/swapclient_server.go index 9318bca..b2eb185 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -750,7 +750,7 @@ func (s *swapClientServer) GetLiquidityParams(_ context.Context, } func newRPCRule(channelID uint64, peer []byte, - rule *liquidity.ThresholdRule) *looprpc.LiquidityRule { + rule *liquidity.SwapRule) *looprpc.LiquidityRule { return &looprpc.LiquidityRule{ ChannelId: channelID, @@ -781,10 +781,10 @@ func (s *swapClientServer) SetLiquidityParams(ctx context.Context, AutoFeeBudget: btcutil.Amount(in.Parameters.AutoloopBudgetSat), MaxAutoInFlight: int(in.Parameters.AutoMaxInFlight), ChannelRules: make( - map[lnwire.ShortChannelID]*liquidity.ThresholdRule, + map[lnwire.ShortChannelID]*liquidity.SwapRule, ), PeerRules: make( - map[route.Vertex]*liquidity.ThresholdRule, + map[route.Vertex]*liquidity.SwapRule, ), ClientRestrictions: liquidity.Restrictions{ Minimum: btcutil.Amount(in.Parameters.MinSwapAmount), @@ -890,16 +890,19 @@ func rpcToFee(req *looprpc.LiquidityParameters) (liquidity.FeeLimit, } // rpcToRule switches on rpc rule type to convert to our rule interface. -func rpcToRule(rule *looprpc.LiquidityRule) (*liquidity.ThresholdRule, error) { +func rpcToRule(rule *looprpc.LiquidityRule) (*liquidity.SwapRule, error) { switch rule.Type { case looprpc.LiquidityRuleType_UNKNOWN: return nil, fmt.Errorf("rule type field must be set") case looprpc.LiquidityRuleType_THRESHOLD: - return liquidity.NewThresholdRule( - int(rule.IncomingThreshold), - int(rule.OutgoingThreshold), - ), nil + return &liquidity.SwapRule{ + ThresholdRule: liquidity.NewThresholdRule( + int(rule.IncomingThreshold), + int(rule.OutgoingThreshold), + ), + Type: swap.TypeOut, + }, nil default: return nil, fmt.Errorf("unknown rule: %T", rule)