liquidity: move swap creation into separate function

pull/333/head
carla 3 years ago
parent 9c35946cab
commit c6e816ad95
No known key found for this signature in database
GPG Key ID: 4CA7FE54A6213C91

@ -4,6 +4,7 @@ import (
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/lndclient"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
// balances summarizes the state of the balances of a channel. Channel reserve,
@ -20,6 +21,9 @@ type balances struct {
// channelID is the channel that has these balances.
channelID lnwire.ShortChannelID
// pubkey is the public key of the peer we have this balances set with.
pubkey route.Vertex
}
// newBalances creates a balances struct from lndclient channel information.
@ -29,5 +33,6 @@ func newBalances(info lndclient.ChannelInfo) *balances {
incoming: info.RemoteBalance,
outgoing: info.LocalBalance,
channelID: lnwire.NewShortChanIDFromInt(info.ChannelID),
pubkey: info.PubKeyBytes,
}
}

@ -705,53 +705,21 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
continue
}
// Check whether we can perform a swap, adding the channel to
// our set of disqualified swaps if it is not eligible.
reason := traffic.maySwap(channel.PubKeyBytes, balance.channelID)
if reason != ReasonNone {
disqualified[balance.channelID] = reason
continue
}
// We can have zero amount in the case where no action is
// required, so we skip over them.
amount := rule.swapAmount(balance, restrictions)
if amount == 0 {
disqualified[balance.channelID] = ReasonLiquidityOk
continue
}
// Get a quote for a swap of this amount.
quote, err := m.cfg.LoopOutQuote(
ctx, &loop.LoopOutQuoteRequest{
Amount: amount,
SweepConfTarget: m.params.SweepConfTarget,
SwapPublicationDeadline: m.cfg.Clock.Now(),
},
suggestion, err := m.suggestSwap(
ctx, traffic, balance, rule, restrictions, autoloop,
)
if err != nil {
return nil, err
}
log.Debugf("quote for suggestion: %v, swap fee: %v, "+
"miner fee: %v, prepay: %v", amount, quote.SwapFee,
quote.MinerFee, quote.PrepayAmount)
// Check that the estimated fees for the suggested swap are
// below the fee limits configured by the manager.
feeReason := m.checkFeeLimits(quote, amount)
if feeReason != ReasonNone {
disqualified[balance.channelID] = feeReason
var reasonErr *reasonError
if errors.As(err, &reasonErr) {
disqualified[balance.channelID] = reasonErr.reason
continue
}
outRequest, err := m.makeLoopOutRequest(
ctx, amount, balance, quote, autoloop,
)
if err != nil {
return nil, err
}
suggestions = append(suggestions, outRequest)
suggestions = append(suggestions, *suggestion)
}
// Finally, run through all possible swaps, excluding swaps that are
@ -824,6 +792,67 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
return resp, nil
}
// 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,
autoloop bool) (*loop.OutRequest, error) {
// Check whether we can perform a swap.
err := traffic.maySwap(balance.pubkey, balance.channelID)
if err != nil {
return nil, err
}
// We can have nil suggestions in the case where no action is
// required, so we skip over them.
amount := rule.swapAmount(balance, restrictions)
if amount == 0 {
return nil, newReasonError(ReasonLiquidityOk)
}
return m.loopOutSwap(ctx, amount, balance, autoloop)
}
// loopOutSwap creates a loop out swap with the amount provided for the balance
// described by the balance set provided. A reason that indicates whether we
// can swap is returned. If this value is not ReasonNone, there is no possible
// swap and the loop out request returned will be nil.
func (m *Manager) loopOutSwap(ctx context.Context, amount btcutil.Amount,
balance *balances, autoloop bool) (*loop.OutRequest, error) {
quote, err := m.cfg.LoopOutQuote(
ctx, &loop.LoopOutQuoteRequest{
Amount: amount,
SweepConfTarget: m.params.SweepConfTarget,
SwapPublicationDeadline: m.cfg.Clock.Now(),
},
)
if err != nil {
return nil, err
}
log.Debugf("quote for suggestion: %v, swap fee: %v, "+
"miner fee: %v, prepay: %v", amount, quote.SwapFee,
quote.MinerFee, quote.PrepayAmount)
// Check that the estimated fees for the suggested swap are
// below the fee limits configured by the manager.
feeReason := m.checkFeeLimits(quote, amount)
if feeReason != ReasonNone {
return nil, newReasonError(feeReason)
}
outRequest, err := m.makeLoopOutRequest(
ctx, amount, balance, quote, autoloop,
)
if err != nil {
return nil, err
}
return &outRequest, nil
}
// 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
@ -1093,31 +1122,31 @@ func newSwapTraffic() *swapTraffic {
// maySwap returns a boolean that indicates whether we may perform a swap for a
// peer and its set of channels.
func (s *swapTraffic) maySwap(peer route.Vertex,
chanID lnwire.ShortChannelID) Reason {
chanID lnwire.ShortChannelID) error {
lastFail, recentFail := s.failedLoopOut[chanID]
if recentFail {
log.Debugf("Channel: %v not eligible for suggestions, was "+
"part of a failed swap at: %v", chanID, lastFail)
return ReasonFailureBackoff
return newReasonError(ReasonFailureBackoff)
}
if s.ongoingLoopOut[chanID] {
log.Debugf("Channel: %v not eligible for suggestions, "+
"ongoing loop out utilizing channel", chanID)
return ReasonLoopOut
return newReasonError(ReasonLoopOut)
}
if s.ongoingLoopIn[peer] {
log.Debugf("Peer: %x not eligible for suggestions ongoing "+
"loop in utilizing peer", peer)
return ReasonLoopIn
return newReasonError(ReasonLoopIn)
}
return ReasonNone
return nil
}
// checkFeeLimits takes a set of fees for a swap and checks whether they exceed

@ -1,5 +1,7 @@
package liquidity
import "fmt"
// Reason is an enum which represents the various reasons we have for not
// executing a swap.
type Reason uint8
@ -60,3 +62,67 @@ const (
// but we have allocated it to other swaps.
ReasonBudgetInsufficient
)
// String returns a string representation of a reason.
func (r Reason) String() string {
switch r {
case ReasonNone:
return "none"
case ReasonBudgetNotStarted:
return "budget not started"
case ReasonSweepFees:
return "sweep fees to high"
case ReasonBudgetElapsed:
return "budget elapsed"
case ReasonInFlight:
return "autoloops already in flight"
case ReasonSwapFee:
return "swap server fee to high"
case ReasonMinerFee:
return "miner fee to high"
case ReasonPrepay:
return "prepayment too high"
case ReasonFailureBackoff:
return "backing off due to failure"
case ReasonLoopOut:
return "loop out using channel"
case ReasonLoopIn:
return "loop in using peer"
case ReasonLiquidityOk:
return "liquidity balance ok"
case ReasonBudgetInsufficient:
return "budget insufficient"
default:
return "unknown"
}
}
// reasonError is an error type which embeds our reasons for not performing
// swaps.
type reasonError struct {
reason Reason
}
func newReasonError(r Reason) *reasonError {
return &reasonError{
reason: r,
}
}
// Error returns an error string for a reason error.
func (r *reasonError) Error() string {
return fmt.Sprintf("swap reason: %v", r.reason)
}

Loading…
Cancel
Save