Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions cmd/loop/liquidity.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,10 @@ var setParamsCommand = cli.Command{
"included in suggestions.",
},
cli.BoolFlag{
Name: "autoout",
Name: "autoloop",
Usage: "set to true to enable automated dispatch " +
"of loop out swaps, limited to the budget " +
"set by autobudget",
"of swaps, limited to the budget set by " +
"autobudget",
},
cli.Uint64Flag{
Name: "autobudget",
Expand Down Expand Up @@ -338,18 +338,18 @@ func setParams(ctx *cli.Context) error {
flagSet = true
}

if ctx.IsSet("autoout") {
params.AutoLoopOut = ctx.Bool("autoout")
if ctx.IsSet("autoloop") {
params.Autoloop = ctx.Bool("autoloop")
flagSet = true
}

if ctx.IsSet("autobudget") {
params.AutoOutBudgetSat = ctx.Uint64("autobudget")
params.AutoloopBudgetSat = ctx.Uint64("autobudget")
flagSet = true
}

if ctx.IsSet("budgetstart") {
params.AutoOutBudgetStartSec = ctx.Uint64("budgetstart")
params.AutoloopBudgetStartSec = ctx.Uint64("budgetstart")
flagSet = true
}

Expand Down
2 changes: 1 addition & 1 deletion docs/autoloop.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ budget of your choosing.
The autoloop functionality is disabled by default, and can be enabled using the
following command:
```
loop setparams --autoout=true
loop setparams --autoloop=true
```

Swaps that are dispatched by the autolooper can be identified in the output of
Expand Down
18 changes: 14 additions & 4 deletions labels/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"errors"
"fmt"
"strings"

"github.com/lightninglabs/loop/swap"
)

const (
Expand All @@ -17,6 +19,10 @@ const (
// autoOut is the label used for loop out swaps that are automatically
// dispatched.
autoOut = "autoloop-out"

// autoIn is the label used for loop in swaps that are automatically
// dispatched.
autoIn = "autoloop-in"
)

var (
Expand All @@ -28,10 +34,14 @@ var (
ErrReservedPrefix = errors.New("label contains reserved prefix")
)

// AutoOutLabel returns a label with the reserved prefix that identifies
// automatically dispatched loop outs.
func AutoOutLabel() string {
return fmt.Sprintf("%v: %v", Reserved, autoOut)
// AutoloopLabel returns a label with the reserved prefix that identifies
// automatically dispatched swaps depending on the type of swap being executed.
func AutoloopLabel(swapType swap.Type) string {
if swapType == swap.TypeOut {
return fmt.Sprintf("%v: %v", Reserved, autoOut)
}

return fmt.Sprintf("%v: %v", Reserved, autoIn)
}

// Validate checks that a label is of appropriate length and is not in our list
Expand Down
7 changes: 4 additions & 3 deletions liquidity/autoloop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
Expand Down Expand Up @@ -75,7 +76,7 @@ func TestAutoLoopEnabled(t *testing.T) {
// is set to allow exactly 2 swaps at the prices that we set in our
// test quotes.
params := Parameters{
AutoOut: true,
Autoloop: true,
AutoFeeBudget: 40066,
AutoFeeStartDate: testTime,
MaxAutoInFlight: 2,
Expand Down Expand Up @@ -145,7 +146,7 @@ func TestAutoLoopEnabled(t *testing.T) {
MaxMinerFee: params.MaximumMinerFee,
SweepConfTarget: params.SweepConfTarget,
OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()},
Label: labels.AutoOutLabel(),
Label: labels.AutoloopLabel(swap.TypeOut),
Initiator: autoloopSwapInitiator,
}

Expand All @@ -161,7 +162,7 @@ func TestAutoLoopEnabled(t *testing.T) {
MaxMinerFee: params.MaximumMinerFee,
SweepConfTarget: params.SweepConfTarget,
OutgoingChanSet: loopdb.ChannelSet{chanID2.ToUint64()},
Label: labels.AutoOutLabel(),
Label: labels.AutoloopLabel(swap.TypeOut),
Initiator: autoloopSwapInitiator,
}

Expand Down
9 changes: 6 additions & 3 deletions liquidity/autoloop_testcontext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/ticker"
Expand Down Expand Up @@ -91,8 +92,10 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters,
testCtx.lnd.Channels = channels

cfg := &Config{
AutoOutTicker: ticker.NewForce(DefaultAutoOutTicker),
LoopOutRestrictions: func(context.Context) (*Restrictions, error) {
AutoloopTicker: ticker.NewForce(DefaultAutoloopTicker),
Restrictions: func(context.Context, swap.Type) (*Restrictions,
error) {

return <-testCtx.loopOutRestrictions, nil
},
ListLoopOut: func() ([]*loopdb.LoopOut, error) {
Expand Down Expand Up @@ -182,7 +185,7 @@ func (c *autoloopTestCtx) autoloop(minAmt, maxAmt btcutil.Amount,
expectedSwaps []loopOutRequestResp) {

// Tick our autoloop ticker to force assessing whether we want to loop.
c.manager.cfg.AutoOutTicker.Force <- testTime
c.manager.cfg.AutoloopTicker.Force <- testTime

// Send a mocked response from the server with the swap size limits.
c.loopOutRestrictions <- NewRestrictions(minAmt, maxAmt)
Expand Down
89 changes: 44 additions & 45 deletions liquidity/liquidity.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/funding"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
Expand Down Expand Up @@ -96,9 +97,9 @@ const (
// suggestions as a dry-run).
defaultMaxInFlight = 1

// DefaultAutoOutTicker is the default amount of time between automated
// loop out checks.
DefaultAutoOutTicker = time.Minute * 10
// DefaultAutoloopTicker is the default amount of time between automated
// swap checks.
DefaultAutoloopTicker = time.Minute * 10

// autoloopSwapInitiator is the value we send in the initiator field of
// a swap request when issuing an automatic swap.
Expand Down Expand Up @@ -182,14 +183,15 @@ var (
// Config contains the external functionality required to run the
// liquidity manager.
type Config struct {
// AutoOutTicker determines how often we should check whether we want
// to dispatch an automated loop out. We use a force ticker so that
// we can trigger autoloop in itests.
AutoOutTicker *ticker.Force
// AutoloopTicker determines how often we should check whether we want
// to dispatch an automated swap. We use a force ticker so that we can
// trigger autoloop in itests.
AutoloopTicker *ticker.Force

// LoopOutRestrictions returns the restrictions that the server applies
// to loop out swaps.
LoopOutRestrictions func(ctx context.Context) (*Restrictions, error)
// Restrictions returns the restrictions that the server applies to
// swaps.
Restrictions func(ctx context.Context, swapType swap.Type) (
*Restrictions, error)

// Lnd provides us with access to lnd's rpc servers.
Lnd *lndclient.LndServices
Expand Down Expand Up @@ -220,8 +222,8 @@ type Config struct {
// Parameters is a set of parameters provided by the user which guide
// how we assess liquidity.
type Parameters struct {
// AutoOut enables automatic dispatch of loop out swaps.
AutoOut bool
// Autoloop enables automatic dispatch of swaps.
Autoloop bool

// AutoFeeBudget is the total amount we allow to be spent on
// automatically dispatched swaps. Once this budget has been used, we
Expand Down Expand Up @@ -431,12 +433,12 @@ type Manager struct {
// We run this loop even if automated swaps are not currently enabled rather
// than managing starting and stopping the ticker as our parameters are updated.
func (m *Manager) Run(ctx context.Context) error {
m.cfg.AutoOutTicker.Resume()
defer m.cfg.AutoOutTicker.Stop()
m.cfg.AutoloopTicker.Resume()
defer m.cfg.AutoloopTicker.Stop()

for {
select {
case <-m.cfg.AutoOutTicker.Ticks():
case <-m.cfg.AutoloopTicker.Ticks():
if err := m.autoloop(ctx); err != nil {
log.Errorf("autoloop failed: %v", err)
}
Expand Down Expand Up @@ -466,7 +468,7 @@ func (m *Manager) GetParameters() Parameters {
// SetParameters updates our current set of parameters if the new parameters
// provided are valid.
func (m *Manager) SetParameters(ctx context.Context, params Parameters) error {
restrictions, err := m.cfg.LoopOutRestrictions(ctx)
restrictions, err := m.cfg.Restrictions(ctx, swap.TypeOut)
if err != nil {
return err
}
Expand Down Expand Up @@ -510,6 +512,15 @@ func (m *Manager) autoloop(ctx context.Context) error {
}

for _, swap := range swaps {
// If we don't actually have dispatch of swaps enabled, log
// suggestions.
if !m.params.Autoloop {
log.Debugf("recommended autoloop: %v sats over "+
"%v", swap.Amount, swap.OutgoingChanSet)

continue
}

// Create a copy of our range var so that we can reference it.
swap := swap
loopOut, err := m.cfg.LoopOut(ctx, &swap)
Expand All @@ -528,7 +539,7 @@ func (m *Manager) autoloop(ctx context.Context) error {
// ForceAutoLoop force-ticks our auto-out ticker.
func (m *Manager) ForceAutoLoop(ctx context.Context) error {
select {
case m.cfg.AutoOutTicker.Force <- m.cfg.Clock.Now():
case m.cfg.AutoloopTicker.Force <- m.cfg.Clock.Now():
return nil

case <-ctx.Done():
Expand All @@ -538,11 +549,11 @@ func (m *Manager) ForceAutoLoop(ctx context.Context) error {

// 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. It takes an autoOut boolean that indicates whether the
// no rules set. It takes an autoloop boolean that indicates whether the
// suggestions are being used for our internal autolooper. This boolean is used
// to determine the information we add to our swap suggestion and whether we
// return any suggestions.
func (m *Manager) SuggestSwaps(ctx context.Context, autoOut bool) (
func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
[]loop.OutRequest, error) {

m.paramsLock.Lock()
Expand Down Expand Up @@ -587,7 +598,7 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoOut bool) (

// Get the current server side restrictions, combined with the client
// set restrictions, if any.
outRestrictions, err := m.getLoopOutRestrictions(ctx)
restrictions, err := m.getSwapRestrictions(ctx, swap.TypeOut)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -646,7 +657,7 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoOut bool) (

balance := newBalances(channel)

suggestion := rule.suggestSwap(balance, outRestrictions)
suggestion := rule.suggestSwap(balance, restrictions)

// We can have nil suggestions in the case where no action is
// required, so we skip over them.
Expand Down Expand Up @@ -681,7 +692,7 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoOut bool) (
}

outRequest, err := m.makeLoopOutRequest(
ctx, suggestion, quote, autoOut,
ctx, suggestion, quote, autoloop,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -728,29 +739,17 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoOut bool) (
}
}

// If we are getting suggestions for automatically dispatched swaps,
// and they are not enabled in our parameters, we just log the swap
// suggestions and return an empty set of suggestions.
if autoOut && !m.params.AutoOut {
for _, swap := range inBudget {
log.Debugf("recommended autoloop: %v sats over "+
"%v", swap.Amount, swap.OutgoingChanSet)
}

return nil, nil
}

return inBudget, nil
}

// getLoopOutRestrictions queries the server for its latest swap size
// restrictions, validates client restrictions (if present) against these
// values and merges the client's custom requirements with the server's limits
// to produce a single set of limitations for our swap.
func (m *Manager) getLoopOutRestrictions(ctx context.Context) (*Restrictions,
error) {
// getSwapRestrictions queries the server for its latest swap size restrictions,
// validates client restrictions (if present) against these values and merges
// the client's custom requirements with the server's limits to produce a single
// set of limitations for our swap.
func (m *Manager) getSwapRestrictions(ctx context.Context, swapType swap.Type) (
*Restrictions, error) {

restrictions, err := m.cfg.LoopOutRestrictions(ctx)
restrictions, err := m.cfg.Restrictions(ctx, swapType)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -791,7 +790,7 @@ func (m *Manager) getLoopOutRestrictions(ctx context.Context) (*Restrictions,
// non-auto requests, because the client api will set it anyway).
func (m *Manager) makeLoopOutRequest(ctx context.Context,
suggestion *LoopOutRecommendation, quote *loop.LoopOutQuote,
autoOut bool) (loop.OutRequest, error) {
autoloop bool) (loop.OutRequest, error) {

prepayMaxFee := ppmToSat(
quote.PrepayAmount, m.params.MaximumPrepayRoutingFeePPM,
Expand All @@ -815,8 +814,8 @@ func (m *Manager) makeLoopOutRequest(ctx context.Context,
Initiator: autoloopSwapInitiator,
}

if autoOut {
request.Label = labels.AutoOutLabel()
if autoloop {
request.Label = labels.AutoloopLabel(swap.TypeOut)

addr, err := m.cfg.Lnd.WalletKit.NextAddr(ctx)
if err != nil {
Expand Down Expand Up @@ -882,7 +881,7 @@ func (m *Manager) checkExistingAutoLoops(ctx context.Context,
var summary existingAutoLoopSummary

for _, out := range loopOuts {
if out.Contract.Label != labels.AutoOutLabel() {
if out.Contract.Label != labels.AutoloopLabel(swap.TypeOut) {
continue
}

Expand Down
Loading