@@ -3,6 +3,7 @@ package loop
33import (
44 "context"
55 "errors"
6+ "fmt"
67 "strings"
78 "sync"
89 "sync/atomic"
@@ -33,20 +34,10 @@ var (
3334 // more than the server maximum.
3435 ErrSwapAmountTooHigh = errors .New ("swap amount too high" )
3536
36- // ErrExpiryTooSoon is returned when the server proposes an expiry that
37- // is too soon for us.
38- ErrExpiryTooSoon = errors .New ("swap expiry too soon" )
39-
4037 // ErrExpiryTooFar is returned when the server proposes an expiry that
4138 // is too soon for us.
4239 ErrExpiryTooFar = errors .New ("swap expiry too far" )
4340
44- // ErrSweepConfTargetTooFar is returned when the client proposes a
45- // confirmation target to sweep the on-chain HTLC of a Loop Out that is
46- // beyond the expiration height proposed by the server.
47- ErrSweepConfTargetTooFar = errors .New ("sweep confirmation target is " +
48- "beyond swap expiration height" )
49-
5041 // serverRPCTimeout is the maximum time a gRPC request to the server
5142 // should be allowed to take.
5243 serverRPCTimeout = 30 * time .Second
@@ -363,8 +354,21 @@ func (s *Client) LoopOut(globalCtx context.Context,
363354 return nil , err
364355 }
365356
366- // Create a new swap object for this swap.
357+ // Calculate htlc expiry height.
358+ terms , err := s .Server .GetLoopOutTerms (globalCtx )
359+ if err != nil {
360+ return nil , err
361+ }
362+
367363 initiationHeight := s .executor .height ()
364+ request .Expiry , err = s .getExpiry (
365+ initiationHeight , terms , request .SweepConfTarget ,
366+ )
367+ if err != nil {
368+ return nil , err
369+ }
370+
371+ // Create a new swap object for this swap.
368372 swapCfg := newSwapConfig (s .lndServices , s .Store , s .Server )
369373 initResult , err := newLoopOutSwap (
370374 globalCtx , swapCfg , initiationHeight , request ,
@@ -386,6 +390,24 @@ func (s *Client) LoopOut(globalCtx context.Context,
386390 }, nil
387391}
388392
393+ // getExpiry returns an absolute expiry height based on the sweep confirmation
394+ // target, constrained by the server terms.
395+ func (s * Client ) getExpiry (height int32 , terms * LoopOutTerms ,
396+ confTarget int32 ) (int32 , error ) {
397+
398+ switch {
399+ case confTarget < terms .MinCltvDelta :
400+ return height + terms .MinCltvDelta , nil
401+
402+ case confTarget > terms .MaxCltvDelta :
403+ return 0 , fmt .Errorf ("confirmation target %v exceeds maximum " +
404+ "server cltv delta of %v" , confTarget ,
405+ terms .MaxCltvDelta )
406+ }
407+
408+ return height + confTarget , nil
409+ }
410+
389411// LoopOutQuote takes a LoopOut amount and returns a break down of estimated
390412// costs for the client. Both the swap server and the on-chain fee estimator
391413// are queried to get to build the quote response.
@@ -405,8 +427,14 @@ func (s *Client) LoopOutQuote(ctx context.Context,
405427 return nil , ErrSwapAmountTooHigh
406428 }
407429
430+ height := s .executor .height ()
431+ expiry , err := s .getExpiry (height , terms , request .SweepConfTarget )
432+ if err != nil {
433+ return nil , err
434+ }
435+
408436 quote , err := s .Server .GetLoopOutQuote (
409- ctx , request .Amount , request .SwapPublicationDeadline ,
437+ ctx , request .Amount , expiry , request .SwapPublicationDeadline ,
410438 )
411439 if err != nil {
412440 return nil , err
@@ -440,7 +468,6 @@ func (s *Client) LoopOutQuote(ctx context.Context,
440468 MinerFee : minerFee ,
441469 PrepayAmount : quote .PrepayAmount ,
442470 SwapPaymentDest : quote .SwapPaymentDest ,
443- CltvDelta : quote .CltvDelta ,
444471 }, nil
445472}
446473
0 commit comments