Browse Source

liquidity: add swap suggestions endpoint to manager

pull/278/head
carla 1 month ago
parent
commit
340766fbb5
No known key found for this signature in database GPG Key ID: 4CA7FE54A6213C91
4 changed files with 153 additions and 0 deletions
  1. +11
    -0
      liquidity/balances.go
  2. +52
    -0
      liquidity/liquidity.go
  3. +89
    -0
      liquidity/liquidity_test.go
  4. +1
    -0
      loopd/utils.go

+ 11
- 0
liquidity/balances.go View File

@ -2,6 +2,7 @@ package liquidity
import (
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/lndclient"
"github.com/lightningnetwork/lnd/lnwire"
)
@ -20,3 +21,13 @@ type balances struct {
// channelID is the channel that has these balances.
channelID lnwire.ShortChannelID
}
// newBalances creates a balances struct from lndclient channel information.
func newBalances(info lndclient.ChannelInfo) *balances {
return &balances{
capacity: info.Capacity,
incoming: info.RemoteBalance,
outgoing: info.LocalBalance,
channelID: lnwire.NewShortChanIDFromInt(info.ChannelID),
}
}

+ 52
- 0
liquidity/liquidity.go View File

@ -9,6 +9,7 @@ import (
"strings"
"sync"
"github.com/lightninglabs/lndclient"
"github.com/lightningnetwork/lnd/lnwire"
)
@ -23,6 +24,9 @@ type Config struct {
// LoopOutRestrictions returns the restrictions that the server applies
// to loop out swaps.
LoopOutRestrictions func(ctx context.Context) (*Restrictions, error)
// Lnd provides us with access to lnd's main rpc.
Lnd lndclient.LightningClient
}
// Parameters is a set of parameters provided by the user which guide
@ -131,3 +135,51 @@ func cloneParameters(params Parameters) Parameters {
return paramCopy
}
// SuggestSwaps returns a set of swap suggestions based on our current liquidity
// balance for the set of rules configured for the manager, failing if there are
// no rules set.
func (m *Manager) SuggestSwaps(ctx context.Context) (
[]*LoopOutRecommendation, error) {
m.paramsLock.Lock()
defer m.paramsLock.Unlock()
// If we have no rules set, exit early to avoid unnecessary calls to
// lnd and the server.
if len(m.params.ChannelRules) == 0 {
return nil, nil
}
channels, err := m.cfg.Lnd.ListChannels(ctx)
if err != nil {
return nil, err
}
// Get the current server side restrictions.
outRestrictions, err := m.cfg.LoopOutRestrictions(ctx)
if err != nil {
return nil, err
}
var suggestions []*LoopOutRecommendation
for _, channel := range channels {
channelID := lnwire.NewShortChanIDFromInt(channel.ChannelID)
rule, ok := m.params.ChannelRules[channelID]
if !ok {
continue
}
balance := newBalances(channel)
suggestion := rule.suggestSwap(balance, outRestrictions)
// We can have nil suggestions in the case where no action is
// required, so only add non-nil suggestions.
if suggestion != nil {
suggestions = append(suggestions, suggestion)
}
}
return suggestions, nil
}

+ 89
- 0
liquidity/liquidity_test.go View File

@ -4,6 +4,8 @@ import (
"context"
"testing"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require"
)
@ -16,6 +18,7 @@ func newTestConfig() *Config {
return NewRestrictions(1, 10000), nil
},
Lnd: test.NewMockLnd().Client,
}
}
@ -64,3 +67,89 @@ func TestParameters(t *testing.T) {
err = manager.SetParameters(expected)
require.Equal(t, ErrZeroChannelID, err)
}
// TestSuggestSwaps tests getting of swap suggestions.
func TestSuggestSwaps(t *testing.T) {
var (
chanID1 = lnwire.NewShortChanIDFromInt(1)
chanID2 = lnwire.NewShortChanIDFromInt(2)
)
tests := []struct {
name string
channels []lndclient.ChannelInfo
parameters Parameters
swaps []*LoopOutRecommendation
}{
{
name: "no rules",
channels: nil,
parameters: newParameters(),
},
{
name: "loop out",
channels: []lndclient.ChannelInfo{
{
ChannelID: 1,
Capacity: 1000,
LocalBalance: 1000,
RemoteBalance: 0,
},
},
parameters: Parameters{
ChannelRules: map[lnwire.ShortChannelID]*ThresholdRule{
chanID1: NewThresholdRule(
10, 10,
),
},
},
swaps: []*LoopOutRecommendation{
{
Channel: chanID1,
Amount: 500,
},
},
},
{
name: "no rule for channel",
channels: []lndclient.ChannelInfo{
{
ChannelID: 1,
Capacity: 1000,
LocalBalance: 0,
RemoteBalance: 1000,
},
},
parameters: Parameters{
ChannelRules: map[lnwire.ShortChannelID]*ThresholdRule{
chanID2: NewThresholdRule(10, 10),
},
},
swaps: nil,
},
}
for _, testCase := range tests {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
cfg := newTestConfig()
// Create a mock lnd with the set of channels set in our
// test case.
mock := test.NewMockLnd()
mock.Channels = testCase.channels
cfg.Lnd = mock.Client
manager := NewManager(cfg)
// Set our test case parameters.
err := manager.SetParameters(testCase.parameters)
require.NoError(t, err)
swaps, err := manager.SuggestSwaps(context.Background())
require.NoError(t, err)
require.Equal(t, testCase.swaps, swaps)
})
}
}

+ 1
- 0
loopd/utils.go View File

@ -46,6 +46,7 @@ func getLiquidityManager(client *loop.Client) *liquidity.Manager {
outTerms.MinSwapAmount, outTerms.MaxSwapAmount,
), nil
},
Lnd: client.LndServices.Client,
}
return liquidity.NewManager(mngrCfg)

Loading…
Cancel
Save