mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
liquidity: move swap creation into separate function
This commit is contained in:
parent
9c35946cab
commit
c6e816ad95
3 changed files with 147 additions and 47 deletions
|
|
@ -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,
|
||||
)
|
||||
|
||||
var reasonErr *reasonError
|
||||
if errors.As(err, &reasonErr) {
|
||||
disqualified[balance.channelID] = reasonErr.reason
|
||||
continue
|
||||
}
|
||||
|
||||
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
|
||||
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…
Add table
Add a link
Reference in a new issue