@@ -162,6 +162,21 @@ var (
162
162
163
163
// ErrZeroInFlight is returned is a zero in flight swaps value is set.
164
164
ErrZeroInFlight = errors .New ("max in flight swaps must be >=0" )
165
+
166
+ // ErrMinimumExceedsMaximumAmt is returned when the minimum configured
167
+ // swap amount is more than the maximum.
168
+ ErrMinimumExceedsMaximumAmt = errors .New ("minimum swap amount " +
169
+ "exceeds maximum" )
170
+
171
+ // ErrMaxExceedsServer is returned if the maximum swap amount set is
172
+ // more than the server offers.
173
+ ErrMaxExceedsServer = errors .New ("maximum swap amount is more than " +
174
+ "server maximum" )
175
+
176
+ // ErrMinLessThanServer is returned if the minimum swap amount set is
177
+ // less than the server minimum.
178
+ ErrMinLessThanServer = errors .New ("minimum swap amount is less than " +
179
+ "server minimum" )
165
180
)
166
181
167
182
// Config contains the external functionality required to run the
@@ -264,6 +279,10 @@ type Parameters struct {
264
279
// sweep during a fee spike.
265
280
MaximumMinerFee btcutil.Amount
266
281
282
+ // ClientRestrictions are the restrictions placed on swap size by the
283
+ // client.
284
+ ClientRestrictions Restrictions
285
+
267
286
// ChannelRules maps a short channel ID to a rule that describes how we
268
287
// would like liquidity to be managed.
269
288
ChannelRules map [lnwire.ShortChannelID ]* ThresholdRule
@@ -283,17 +302,19 @@ func (p Parameters) String() string {
283
302
"fee rate limit: %v, sweep conf target: %v, maximum prepay: " +
284
303
"%v, maximum miner fee: %v, maximum swap fee ppm: %v, maximum " +
285
304
"routing fee ppm: %v, maximum prepay routing fee ppm: %v, " +
286
- "auto budget: %v, budget start: %v, max auto in flight: %v" ,
305
+ "auto budget: %v, budget start: %v, max auto in flight: %v, " +
306
+ "minimum swap size=%v, maximum swap size=%v" ,
287
307
strings .Join (channelRules , "," ), p .FailureBackOff ,
288
308
p .SweepFeeRateLimit , p .SweepConfTarget , p .MaximumPrepay ,
289
309
p .MaximumMinerFee , p .MaximumSwapFeePPM ,
290
310
p .MaximumRoutingFeePPM , p .MaximumPrepayRoutingFeePPM ,
291
- p .AutoFeeBudget , p .AutoFeeStartDate , p .MaxAutoInFlight )
311
+ p .AutoFeeBudget , p .AutoFeeStartDate , p .MaxAutoInFlight ,
312
+ p .ClientRestrictions .Minimum , p .ClientRestrictions .Maximum )
292
313
}
293
314
294
315
// validate checks whether a set of parameters is valid. It takes the minimum
295
316
// confirmations we allow for sweep confirmation target as a parameter.
296
- func (p Parameters ) validate (minConfs int32 ) error {
317
+ func (p Parameters ) validate (minConfs int32 , server * Restrictions ) error {
297
318
for channel , rule := range p .ChannelRules {
298
319
if channel .ToUint64 () == 0 {
299
320
return ErrZeroChannelID
@@ -347,6 +368,47 @@ func (p Parameters) validate(minConfs int32) error {
347
368
return ErrZeroInFlight
348
369
}
349
370
371
+ err := validateRestrictions (server , & p .ClientRestrictions )
372
+ if err != nil {
373
+ return err
374
+ }
375
+
376
+ return nil
377
+ }
378
+
379
+ // validateRestrictions checks that client restrictions fall within the server's
380
+ // restrictions.
381
+ func validateRestrictions (server , client * Restrictions ) error {
382
+ zeroMin := client .Minimum == 0
383
+ zeroMax := client .Maximum == 0
384
+
385
+ if zeroMin && zeroMax {
386
+ return nil
387
+ }
388
+
389
+ // If we have a non-zero maximum, we need to ensure it is greater than
390
+ // our minimum (which is fine if min is zero), and does not exceed the
391
+ // server's maximum.
392
+ if ! zeroMax {
393
+ if client .Minimum > client .Maximum {
394
+ return ErrMinimumExceedsMaximumAmt
395
+ }
396
+
397
+ if client .Maximum > server .Maximum {
398
+ return ErrMaxExceedsServer
399
+ }
400
+ }
401
+
402
+ if zeroMin {
403
+ return nil
404
+ }
405
+
406
+ // If the client set a minimum, ensure it is at least equal to the
407
+ // server's limit.
408
+ if client .Minimum < server .Minimum {
409
+ return ErrMinLessThanServer
410
+ }
411
+
350
412
return nil
351
413
}
352
414
@@ -403,8 +465,14 @@ func (m *Manager) GetParameters() Parameters {
403
465
404
466
// SetParameters updates our current set of parameters if the new parameters
405
467
// provided are valid.
406
- func (m * Manager ) SetParameters (params Parameters ) error {
407
- if err := params .validate (m .cfg .MinimumConfirmations ); err != nil {
468
+ func (m * Manager ) SetParameters (ctx context.Context , params Parameters ) error {
469
+ restrictions , err := m .cfg .LoopOutRestrictions (ctx )
470
+ if err != nil {
471
+ return err
472
+ }
473
+
474
+ err = params .validate (m .cfg .MinimumConfirmations , restrictions )
475
+ if err != nil {
408
476
return err
409
477
}
410
478
@@ -517,8 +585,9 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoOut bool) (
517
585
return nil , nil
518
586
}
519
587
520
- // Get the current server side restrictions.
521
- outRestrictions , err := m .cfg .LoopOutRestrictions (ctx )
588
+ // Get the current server side restrictions, combined with the client
589
+ // set restrictions, if any.
590
+ outRestrictions , err := m .getLoopOutRestrictions (ctx )
522
591
if err != nil {
523
592
return nil , err
524
593
}
@@ -674,6 +743,41 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoOut bool) (
674
743
return inBudget , nil
675
744
}
676
745
746
+ // getLoopOutRestrictions queries the server for its latest swap size
747
+ // restrictions, validates client restrictions (if present) against these
748
+ // values and merges the client's custom requirements with the server's limits
749
+ // to produce a single set of limitations for our swap.
750
+ func (m * Manager ) getLoopOutRestrictions (ctx context.Context ) (* Restrictions ,
751
+ error ) {
752
+
753
+ restrictions , err := m .cfg .LoopOutRestrictions (ctx )
754
+ if err != nil {
755
+ return nil , err
756
+ }
757
+
758
+ // It is possible that the server has updated its restrictions since
759
+ // we validated our client restrictions, so we validate again to ensure
760
+ // that our restrictions are within the server's bounds.
761
+ err = validateRestrictions (restrictions , & m .params .ClientRestrictions )
762
+ if err != nil {
763
+ return nil , err
764
+ }
765
+
766
+ // If our minimum is more than the server's minimum, we set it.
767
+ if m .params .ClientRestrictions .Minimum > restrictions .Minimum {
768
+ restrictions .Minimum = m .params .ClientRestrictions .Minimum
769
+ }
770
+
771
+ // If our maximum set and is less than the server's maximum, we set it.
772
+ if m .params .ClientRestrictions .Maximum != 0 &&
773
+ m .params .ClientRestrictions .Maximum < restrictions .Maximum {
774
+
775
+ restrictions .Maximum = m .params .ClientRestrictions .Maximum
776
+ }
777
+
778
+ return restrictions , nil
779
+ }
780
+
677
781
// makeLoopOutRequest creates a loop out request from a suggestion. Since we
678
782
// do not get any information about our off-chain routing fees when we request
679
783
// a quote, we just set our prepay and route maximum fees directly from the
0 commit comments