Skip to content

Commit c6e816a

Browse files
committed
liquidity: move swap creation into separate function
1 parent 9c35946 commit c6e816a

File tree

3 files changed

+144
-44
lines changed

3 files changed

+144
-44
lines changed

liquidity/balances.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/btcsuite/btcutil"
55
"github.com/lightninglabs/lndclient"
66
"github.com/lightningnetwork/lnd/lnwire"
7+
"github.com/lightningnetwork/lnd/routing/route"
78
)
89

910
// balances summarizes the state of the balances of a channel. Channel reserve,
@@ -20,6 +21,9 @@ type balances struct {
2021

2122
// channelID is the channel that has these balances.
2223
channelID lnwire.ShortChannelID
24+
25+
// pubkey is the public key of the peer we have this balances set with.
26+
pubkey route.Vertex
2327
}
2428

2529
// newBalances creates a balances struct from lndclient channel information.
@@ -29,5 +33,6 @@ func newBalances(info lndclient.ChannelInfo) *balances {
2933
incoming: info.RemoteBalance,
3034
outgoing: info.LocalBalance,
3135
channelID: lnwire.NewShortChanIDFromInt(info.ChannelID),
36+
pubkey: info.PubKeyBytes,
3237
}
3338
}

liquidity/liquidity.go

Lines changed: 73 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -705,53 +705,21 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
705705
continue
706706
}
707707

708-
// Check whether we can perform a swap, adding the channel to
709-
// our set of disqualified swaps if it is not eligible.
710-
reason := traffic.maySwap(channel.PubKeyBytes, balance.channelID)
711-
if reason != ReasonNone {
712-
disqualified[balance.channelID] = reason
713-
continue
714-
}
715-
716-
// We can have zero amount in the case where no action is
717-
// required, so we skip over them.
718-
amount := rule.swapAmount(balance, restrictions)
719-
if amount == 0 {
720-
disqualified[balance.channelID] = ReasonLiquidityOk
721-
continue
722-
}
723-
724-
// Get a quote for a swap of this amount.
725-
quote, err := m.cfg.LoopOutQuote(
726-
ctx, &loop.LoopOutQuoteRequest{
727-
Amount: amount,
728-
SweepConfTarget: m.params.SweepConfTarget,
729-
SwapPublicationDeadline: m.cfg.Clock.Now(),
730-
},
708+
suggestion, err := m.suggestSwap(
709+
ctx, traffic, balance, rule, restrictions, autoloop,
731710
)
732-
if err != nil {
733-
return nil, err
734-
}
735711

736-
log.Debugf("quote for suggestion: %v, swap fee: %v, "+
737-
"miner fee: %v, prepay: %v", amount, quote.SwapFee,
738-
quote.MinerFee, quote.PrepayAmount)
739-
740-
// Check that the estimated fees for the suggested swap are
741-
// below the fee limits configured by the manager.
742-
feeReason := m.checkFeeLimits(quote, amount)
743-
if feeReason != ReasonNone {
744-
disqualified[balance.channelID] = feeReason
712+
var reasonErr *reasonError
713+
if errors.As(err, &reasonErr) {
714+
disqualified[balance.channelID] = reasonErr.reason
745715
continue
746716
}
747717

748-
outRequest, err := m.makeLoopOutRequest(
749-
ctx, amount, balance, quote, autoloop,
750-
)
751718
if err != nil {
752719
return nil, err
753720
}
754-
suggestions = append(suggestions, outRequest)
721+
722+
suggestions = append(suggestions, *suggestion)
755723
}
756724

757725
// Finally, run through all possible swaps, excluding swaps that are
@@ -824,6 +792,67 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
824792
return resp, nil
825793
}
826794

795+
// suggestSwap checks whether we can currently perform a swap, and creates a
796+
// swap request for the rule provided.
797+
func (m *Manager) suggestSwap(ctx context.Context, traffic *swapTraffic,
798+
balance *balances, rule *ThresholdRule, restrictions *Restrictions,
799+
autoloop bool) (*loop.OutRequest, error) {
800+
801+
// Check whether we can perform a swap.
802+
err := traffic.maySwap(balance.pubkey, balance.channelID)
803+
if err != nil {
804+
return nil, err
805+
}
806+
807+
// We can have nil suggestions in the case where no action is
808+
// required, so we skip over them.
809+
amount := rule.swapAmount(balance, restrictions)
810+
if amount == 0 {
811+
return nil, newReasonError(ReasonLiquidityOk)
812+
}
813+
814+
return m.loopOutSwap(ctx, amount, balance, autoloop)
815+
}
816+
817+
// loopOutSwap creates a loop out swap with the amount provided for the balance
818+
// described by the balance set provided. A reason that indicates whether we
819+
// can swap is returned. If this value is not ReasonNone, there is no possible
820+
// swap and the loop out request returned will be nil.
821+
func (m *Manager) loopOutSwap(ctx context.Context, amount btcutil.Amount,
822+
balance *balances, autoloop bool) (*loop.OutRequest, error) {
823+
824+
quote, err := m.cfg.LoopOutQuote(
825+
ctx, &loop.LoopOutQuoteRequest{
826+
Amount: amount,
827+
SweepConfTarget: m.params.SweepConfTarget,
828+
SwapPublicationDeadline: m.cfg.Clock.Now(),
829+
},
830+
)
831+
if err != nil {
832+
return nil, err
833+
}
834+
835+
log.Debugf("quote for suggestion: %v, swap fee: %v, "+
836+
"miner fee: %v, prepay: %v", amount, quote.SwapFee,
837+
quote.MinerFee, quote.PrepayAmount)
838+
839+
// Check that the estimated fees for the suggested swap are
840+
// below the fee limits configured by the manager.
841+
feeReason := m.checkFeeLimits(quote, amount)
842+
if feeReason != ReasonNone {
843+
return nil, newReasonError(feeReason)
844+
}
845+
846+
outRequest, err := m.makeLoopOutRequest(
847+
ctx, amount, balance, quote, autoloop,
848+
)
849+
if err != nil {
850+
return nil, err
851+
}
852+
853+
return &outRequest, nil
854+
}
855+
827856
// getSwapRestrictions queries the server for its latest swap size restrictions,
828857
// validates client restrictions (if present) against these values and merges
829858
// the client's custom requirements with the server's limits to produce a single
@@ -1093,31 +1122,31 @@ func newSwapTraffic() *swapTraffic {
10931122
// maySwap returns a boolean that indicates whether we may perform a swap for a
10941123
// peer and its set of channels.
10951124
func (s *swapTraffic) maySwap(peer route.Vertex,
1096-
chanID lnwire.ShortChannelID) Reason {
1125+
chanID lnwire.ShortChannelID) error {
10971126

10981127
lastFail, recentFail := s.failedLoopOut[chanID]
10991128
if recentFail {
11001129
log.Debugf("Channel: %v not eligible for suggestions, was "+
11011130
"part of a failed swap at: %v", chanID, lastFail)
11021131

1103-
return ReasonFailureBackoff
1132+
return newReasonError(ReasonFailureBackoff)
11041133
}
11051134

11061135
if s.ongoingLoopOut[chanID] {
11071136
log.Debugf("Channel: %v not eligible for suggestions, "+
11081137
"ongoing loop out utilizing channel", chanID)
11091138

1110-
return ReasonLoopOut
1139+
return newReasonError(ReasonLoopOut)
11111140
}
11121141

11131142
if s.ongoingLoopIn[peer] {
11141143
log.Debugf("Peer: %x not eligible for suggestions ongoing "+
11151144
"loop in utilizing peer", peer)
11161145

1117-
return ReasonLoopIn
1146+
return newReasonError(ReasonLoopIn)
11181147
}
11191148

1120-
return ReasonNone
1149+
return nil
11211150
}
11221151

11231152
// checkFeeLimits takes a set of fees for a swap and checks whether they exceed

liquidity/reasons.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package liquidity
22

3+
import "fmt"
4+
35
// Reason is an enum which represents the various reasons we have for not
46
// executing a swap.
57
type Reason uint8
@@ -60,3 +62,67 @@ const (
6062
// but we have allocated it to other swaps.
6163
ReasonBudgetInsufficient
6264
)
65+
66+
// String returns a string representation of a reason.
67+
func (r Reason) String() string {
68+
switch r {
69+
case ReasonNone:
70+
return "none"
71+
72+
case ReasonBudgetNotStarted:
73+
return "budget not started"
74+
75+
case ReasonSweepFees:
76+
return "sweep fees to high"
77+
78+
case ReasonBudgetElapsed:
79+
return "budget elapsed"
80+
81+
case ReasonInFlight:
82+
return "autoloops already in flight"
83+
84+
case ReasonSwapFee:
85+
return "swap server fee to high"
86+
87+
case ReasonMinerFee:
88+
return "miner fee to high"
89+
90+
case ReasonPrepay:
91+
return "prepayment too high"
92+
93+
case ReasonFailureBackoff:
94+
return "backing off due to failure"
95+
96+
case ReasonLoopOut:
97+
return "loop out using channel"
98+
99+
case ReasonLoopIn:
100+
return "loop in using peer"
101+
102+
case ReasonLiquidityOk:
103+
return "liquidity balance ok"
104+
105+
case ReasonBudgetInsufficient:
106+
return "budget insufficient"
107+
108+
default:
109+
return "unknown"
110+
}
111+
}
112+
113+
// reasonError is an error type which embeds our reasons for not performing
114+
// swaps.
115+
type reasonError struct {
116+
reason Reason
117+
}
118+
119+
func newReasonError(r Reason) *reasonError {
120+
return &reasonError{
121+
reason: r,
122+
}
123+
}
124+
125+
// Error returns an error string for a reason error.
126+
func (r *reasonError) Error() string {
127+
return fmt.Sprintf("swap reason: %v", r.reason)
128+
}

0 commit comments

Comments
 (0)