@@ -88,6 +88,12 @@ const (
8888 // defaultSweepFeeRateLimit is the default limit we place on estimated
8989 // sweep fees, (750 * 4 /1000 = 3 sat/vByte).
9090 defaultSweepFeeRateLimit = chainfee .SatPerKWeight (750 )
91+
92+ // defaultMaxInFlight is the default number of in-flight automatically
93+ // dispatched swaps we allow. Note that this does not enable automated
94+ // swaps itself (because we want non-zero values to be expressed in
95+ // suggestions as a dry-run).
96+ defaultMaxInFlight = 2
9197)
9298
9399var (
@@ -106,6 +112,7 @@ var (
106112 // liquidity manger with.
107113 defaultParameters = Parameters {
108114 AutoFeeBudget : defaultBudget ,
115+ MaxAutoInFlight : defaultMaxInFlight ,
109116 ChannelRules : make (map [lnwire.ShortChannelID ]* ThresholdRule ),
110117 FailureBackOff : defaultFailureBackoff ,
111118 SweepFeeRateLimit : defaultSweepFeeRateLimit ,
@@ -143,6 +150,9 @@ var (
143150
144151 // ErrNegativeBudget is returned if a negative swap budget is set.
145152 ErrNegativeBudget = errors .New ("swap budget must be >= 0" )
153+
154+ // ErrZeroInFlight is returned is a zero in flight swaps value is set.
155+ ErrZeroInFlight = errors .New ("max in flight swaps must be >=0" )
146156)
147157
148158// Config contains the external functionality required to run the
@@ -187,6 +197,10 @@ type Parameters struct {
187197 // dispatched swaps in our current budget, inclusive.
188198 AutoFeeStartDate time.Time
189199
200+ // MaxAutoInFlight is the maximum number of in-flight automatically
201+ // dispatched swaps we allow.
202+ MaxAutoInFlight int
203+
190204 // FailureBackOff is the amount of time that we require passes after a
191205 // channel has been part of a failed loop out swap before we suggest
192206 // using it again.
@@ -248,12 +262,12 @@ func (p Parameters) String() string {
248262 "fee rate limit: %v, sweep conf target: %v, maximum prepay: " +
249263 "%v, maximum miner fee: %v, maximum swap fee ppm: %v, maximum " +
250264 "routing fee ppm: %v, maximum prepay routing fee ppm: %v, " +
251- "auto budget: %v, budget start: %v" ,
265+ "auto budget: %v, budget start: %v, max auto in flight: %v " ,
252266 strings .Join (channelRules , "," ), p .FailureBackOff ,
253267 p .SweepFeeRateLimit , p .SweepConfTarget , p .MaximumPrepay ,
254268 p .MaximumMinerFee , p .MaximumSwapFeePPM ,
255269 p .MaximumRoutingFeePPM , p .MaximumPrepayRoutingFeePPM ,
256- p .AutoFeeBudget , p .AutoFeeStartDate )
270+ p .AutoFeeBudget , p .AutoFeeStartDate , p . MaxAutoInFlight )
257271}
258272
259273// validate checks whether a set of parameters is valid. It takes the minimum
@@ -308,6 +322,10 @@ func (p Parameters) validate(minConfs int32) error {
308322 return ErrNegativeBudget
309323 }
310324
325+ if p .MaxAutoInFlight <= 0 {
326+ return ErrZeroInFlight
327+ }
328+
311329 return nil
312330}
313331
@@ -456,6 +474,15 @@ func (m *Manager) SuggestSwaps(ctx context.Context) (
456474 return nil , nil
457475 }
458476
477+ // If we have already reached our total allowed number of in flight
478+ // swaps, we do not suggest any more at the moment.
479+ allowedSwaps := m .params .MaxAutoInFlight - summary .inFlightCount
480+ if allowedSwaps <= 0 {
481+ log .Debugf ("%v autoloops allowed, %v in flight" ,
482+ m .params .MaxAutoInFlight , summary .inFlightCount )
483+ return nil , nil
484+ }
485+
459486 eligible , err := m .getEligibleChannels (ctx , loopOut , loopIn )
460487 if err != nil {
461488 return nil , err
@@ -541,8 +568,9 @@ func (m *Manager) SuggestSwaps(ctx context.Context) (
541568 inBudget = append (inBudget , swap )
542569 }
543570
544- // If we're out of budget, exit early.
545- if available == 0 {
571+ // If we're out of budget, or we have hit the max number of
572+ // swaps that we want to dispatch at one time, exit early.
573+ if available == 0 || allowedSwaps == len (inBudget ) {
546574 break
547575 }
548576 }
@@ -612,6 +640,13 @@ type existingAutoLoopSummary struct {
612640 // pendingFees is the worst-case amount of fees we could spend on in
613641 // flight autoloops.
614642 pendingFees btcutil.Amount
643+
644+ // inFlightCount is the total number of automated swaps that are
645+ // currently in flight. Note that this may race with swap completion,
646+ // but not with initiation of new automated swaps, this is ok, because
647+ // it can only lead to dispatching fewer swaps than we could have (not
648+ // too many).
649+ inFlightCount int
615650}
616651
617652// totalFees returns the total amount of fees that automatically dispatched
@@ -622,7 +657,8 @@ func (e *existingAutoLoopSummary) totalFees() btcutil.Amount {
622657
623658// checkExistingAutoLoops calculates the total amount that has been spent by
624659// automatically dispatched swaps that have completed, and the worst-case fee
625- // total for our set of ongoing, automatically dispatched swaps.
660+ // total for our set of ongoing, automatically dispatched swaps as well as a
661+ // current in-flight count.
626662func (m * Manager ) checkExistingAutoLoops (ctx context.Context ,
627663 loopOuts []* loopdb.LoopOut ) (* existingAutoLoopSummary , error ) {
628664
@@ -642,6 +678,8 @@ func (m *Manager) checkExistingAutoLoops(ctx context.Context,
642678 // for the swap provided that the swap completed after our
643679 // budget start date.
644680 if out .State ().State .Type () == loopdb .StateTypePending {
681+ summary .inFlightCount ++
682+
645683 prepay , err := m .cfg .Lnd .Client .DecodePaymentRequest (
646684 ctx , out .Contract .PrepayInvoice ,
647685 )
0 commit comments