@@ -81,6 +81,12 @@ const (
8181 // autoloopSwapInitiator is the value we send in the initiator field of
8282 // a swap request when issuing an automatic swap.
8383 autoloopSwapInitiator = "autoloop"
84+
85+ // We use a static fee rate to estimate our sweep fee, because we
86+ // can't realistically estimate what our fee estimate will be by the
87+ // time we reach timeout. We set this to a high estimate so that we can
88+ // account for worst-case fees, (1250 * 4 / 1000) = 50 sat/byte.
89+ defaultLoopInSweepFee = chainfee .SatPerKWeight (1250 )
8490)
8591
8692var (
97103 defaultParameters = Parameters {
98104 AutoFeeBudget : defaultBudget ,
99105 MaxAutoInFlight : defaultMaxInFlight ,
100- ChannelRules : make (map [lnwire.ShortChannelID ]* ThresholdRule ),
101- PeerRules : make (map [route.Vertex ]* ThresholdRule ),
106+ ChannelRules : make (map [lnwire.ShortChannelID ]* SwapRule ),
107+ PeerRules : make (map [route.Vertex ]* SwapRule ),
102108 FailureBackOff : defaultFailureBackoff ,
103109 SweepConfTarget : defaultConfTarget ,
104110 FeeLimit : defaultFeePortion (),
@@ -216,13 +222,13 @@ type Parameters struct {
216222 // ChannelRules maps a short channel ID to a rule that describes how we
217223 // would like liquidity to be managed. These rules and PeerRules are
218224 // exclusively set to prevent overlap between peer and channel rules.
219- ChannelRules map [lnwire.ShortChannelID ]* ThresholdRule
225+ ChannelRules map [lnwire.ShortChannelID ]* SwapRule
220226
221227 // PeerRules maps a peer's pubkey to a rule that applies to all the
222228 // channels that we have with the peer collectively. These rules and
223229 // ChannelRules are exclusively set to prevent overlap between peer
224230 // and channel rules map to avoid ambiguity.
225- PeerRules map [route.Vertex ]* ThresholdRule
231+ PeerRules map [route.Vertex ]* SwapRule
226232}
227233
228234// String returns the string representation of our parameters.
@@ -386,10 +392,6 @@ type Manager struct {
386392 // current liquidity balance.
387393 cfg * Config
388394
389- // builder is the swap builder responsible for creating swaps of our
390- // chosen type for us.
391- builder swapBuilder
392-
393395 // params is the set of parameters we are currently using. These may be
394396 // updated at runtime.
395397 params Parameters
@@ -428,9 +430,8 @@ func (m *Manager) Run(ctx context.Context) error {
428430// NewManager creates a liquidity manager which has no rules set.
429431func NewManager (cfg * Config ) * Manager {
430432 return & Manager {
431- cfg : cfg ,
432- params : defaultParameters ,
433- builder : newLoopOutBuilder (cfg ),
433+ cfg : cfg ,
434+ params : defaultParameters ,
434435 }
435436}
436437
@@ -473,7 +474,7 @@ func (m *Manager) SetParameters(ctx context.Context, params Parameters) error {
473474func cloneParameters (params Parameters ) Parameters {
474475 paramCopy := params
475476 paramCopy .ChannelRules = make (
476- map [lnwire.ShortChannelID ]* ThresholdRule ,
477+ map [lnwire.ShortChannelID ]* SwapRule ,
477478 len (params .ChannelRules ),
478479 )
479480
@@ -483,7 +484,7 @@ func cloneParameters(params Parameters) Parameters {
483484 }
484485
485486 paramCopy .PeerRules = make (
486- map [route.Vertex ]* ThresholdRule ,
487+ map [route.Vertex ]* SwapRule ,
487488 len (params .PeerRules ),
488489 )
489490
@@ -617,23 +618,8 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
617618 return m .singleReasonSuggestion (ReasonBudgetNotStarted ), nil
618619 }
619620
620- // Before we get any swap suggestions, we check what the current fee
621- // estimate is to sweep within our target number of confirmations. If
622- // This fee exceeds the fee limit we have set, we will not suggest any
623- // swaps at present.
624- if err := m .builder .maySwap (ctx , m .params ); err != nil {
625- var reasonErr * reasonError
626- if errors .As (err , & reasonErr ) {
627- return m .singleReasonSuggestion (reasonErr .reason ), nil
628-
629- }
630-
631- return nil , err
632- }
633-
634- // Get the current server side restrictions, combined with the client
635- // set restrictions, if any.
636- restrictions , err := m .getSwapRestrictions (ctx , m .builder .swapType ())
621+ // Get restrictions placed on swaps by the server.
622+ outRestrictions , err := m .getSwapRestrictions (ctx , swap .TypeOut )
637623 if err != nil {
638624 return nil , err
639625 }
@@ -653,7 +639,7 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
653639
654640 // Get a summary of our existing swaps so that we can check our autoloop
655641 // budget.
656- summary , err := m .checkExistingAutoLoops (ctx , loopOut )
642+ summary , err := m .checkExistingAutoLoops (ctx , loopOut , loopIn )
657643 if err != nil {
658644 return nil , err
659645 }
@@ -721,7 +707,8 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
721707 }
722708
723709 suggestion , err := m .suggestSwap (
724- ctx , traffic , balances , rule , restrictions , autoloop ,
710+ ctx , traffic , balances , rule , outRestrictions ,
711+ autoloop ,
725712 )
726713 var reasonErr * reasonError
727714 if errors .As (err , & reasonErr ) {
@@ -746,7 +733,8 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
746733 }
747734
748735 suggestion , err := m .suggestSwap (
749- ctx , traffic , balance , rule , restrictions , autoloop ,
736+ ctx , traffic , balance , rule , outRestrictions ,
737+ autoloop ,
750738 )
751739
752740 var reasonErr * reasonError
@@ -841,12 +829,34 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
841829// suggestSwap checks whether we can currently perform a swap, and creates a
842830// swap request for the rule provided.
843831func (m * Manager ) suggestSwap (ctx context.Context , traffic * swapTraffic ,
844- balance * balances , rule * ThresholdRule , restrictions * Restrictions ,
832+ balance * balances , rule * SwapRule , outRestrictions * Restrictions ,
845833 autoloop bool ) (swapSuggestion , error ) {
846834
835+ var (
836+ builder swapBuilder
837+ restrictions * Restrictions
838+ )
839+
840+ switch rule .Type {
841+ case swap .TypeOut :
842+ builder = newLoopOutBuilder (m .cfg )
843+ restrictions = outRestrictions
844+
845+ default :
846+ return nil , fmt .Errorf ("unsupported swap type: %v" , rule .Type )
847+ }
848+
849+ // Before we get any swap suggestions, we check what the current fee
850+ // estimate is to sweep within our target number of confirmations. If
851+ // This fee exceeds the fee limit we have set, we will not suggest any
852+ // swaps at present.
853+ if err := builder .maySwap (ctx , m .params ); err != nil {
854+ return nil , err
855+ }
856+
847857 // First, check whether this peer/channel combination is already in use
848858 // for our swap.
849- err := m . builder .inUse (traffic , balance .pubkey , balance .channels )
859+ err := builder .inUse (traffic , balance .pubkey , balance .channels )
850860 if err != nil {
851861 return nil , err
852862 }
@@ -858,7 +868,7 @@ func (m *Manager) suggestSwap(ctx context.Context, traffic *swapTraffic,
858868 return nil , newReasonError (ReasonLiquidityOk )
859869 }
860870
861- return m . builder .buildSwap (
871+ return builder .buildSwap (
862872 ctx , balance .pubkey , balance .channels , amount , autoloop ,
863873 m .params ,
864874 )
@@ -948,7 +958,8 @@ func (e *existingAutoLoopSummary) totalFees() btcutil.Amount {
948958// total for our set of ongoing, automatically dispatched swaps as well as a
949959// current in-flight count.
950960func (m * Manager ) checkExistingAutoLoops (ctx context.Context ,
951- loopOuts []* loopdb.LoopOut ) (* existingAutoLoopSummary , error ) {
961+ loopOuts []* loopdb.LoopOut , loopIns []* loopdb.LoopIn ) (
962+ * existingAutoLoopSummary , error ) {
952963
953964 var summary existingAutoLoopSummary
954965
@@ -987,6 +998,28 @@ func (m *Manager) checkExistingAutoLoops(ctx context.Context,
987998 }
988999 }
9891000
1001+ for _ , in := range loopIns {
1002+ if in .Contract .Label != labels .AutoloopLabel (swap .TypeIn ) {
1003+ continue
1004+ }
1005+
1006+ pending := in .State ().State .Type () == loopdb .StateTypePending
1007+ inBudget := ! in .LastUpdateTime ().Before (m .params .AutoFeeStartDate )
1008+
1009+ // If an autoloop is in a pending state, we always count it in
1010+ // our current budget, and record the worst-case fees for it,
1011+ // because we do not know how it will resolve.
1012+ if pending {
1013+ summary .inFlightCount ++
1014+ summary .pendingFees += worstCaseInFees (
1015+ in .Contract .MaxMinerFee , in .Contract .MaxSwapFee ,
1016+ defaultLoopInSweepFee ,
1017+ )
1018+ } else if inBudget {
1019+ summary .spentFees += in .State ().Cost .Total ()
1020+ }
1021+ }
1022+
9901023 return & summary , nil
9911024}
9921025
@@ -1051,17 +1084,28 @@ func (m *Manager) currentSwapTraffic(loopOut []*loopdb.LoopOut,
10511084 }
10521085
10531086 for _ , in := range loopIn {
1054- // Skip completed swaps, they can't affect our channel balances.
1055- if in .State ().State .Type () != loopdb .StateTypePending {
1056- continue
1057- }
1058-
10591087 // Skip over swaps that may come through any peer.
10601088 if in .Contract .LastHop == nil {
10611089 continue
10621090 }
10631091
1064- traffic .ongoingLoopIn [* in .Contract .LastHop ] = true
1092+ pubkey := * in .Contract .LastHop
1093+
1094+ switch {
1095+ // Include any pending swaps in our ongoing set of swaps.
1096+ case in .State ().State .Type () == loopdb .StateTypePending :
1097+ traffic .ongoingLoopIn [pubkey ] = true
1098+
1099+ // If a swap failed with an on-chain timeout, the server could
1100+ // not route to us. We add it to our backoff list so that
1101+ // there's some time for routing conditions to improve.
1102+ case in .State ().State == loopdb .StateFailTimeout :
1103+ failedAt := in .LastUpdate ().Time
1104+
1105+ if failedAt .After (failureCutoff ) {
1106+ traffic .failedLoopIn [pubkey ] = failedAt
1107+ }
1108+ }
10651109 }
10661110
10671111 return traffic
@@ -1072,13 +1116,15 @@ type swapTraffic struct {
10721116 ongoingLoopOut map [lnwire.ShortChannelID ]bool
10731117 ongoingLoopIn map [route.Vertex ]bool
10741118 failedLoopOut map [lnwire.ShortChannelID ]time.Time
1119+ failedLoopIn map [route.Vertex ]time.Time
10751120}
10761121
10771122func newSwapTraffic () * swapTraffic {
10781123 return & swapTraffic {
10791124 ongoingLoopOut : make (map [lnwire.ShortChannelID ]bool ),
10801125 ongoingLoopIn : make (map [route.Vertex ]bool ),
10811126 failedLoopOut : make (map [lnwire.ShortChannelID ]time.Time ),
1127+ failedLoopIn : make (map [route.Vertex ]time.Time ),
10821128 }
10831129}
10841130
0 commit comments