From 47321ba4a4299ee79d9648f69dc9fa22c3ccc27d Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Tue, 25 Jun 2019 14:41:57 -0400 Subject: [PATCH] loop: use default confirmation target for htlc sweep after expiry delta In this commit, we introduce a delta from the on-chain HTLC's expiration. Since clients are now able to specify a confirmation target for the HTLC sweep, it's possible that the sweep doesn't confirm within the intended target in the event of an increasing demand for block space. Once the delta from the HTLC's expiration is reached, we'll begin to use the default sweep confirmation target when sweeping the HTLC, which should provide a timely confirmation. This is needed because if the preimage has already been revealed, then we need to make sure we sweep the HTLC before the server does. --- client.go | 6 ++++++ loopout.go | 37 ++++++++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/client.go b/client.go index 89abf7c..8814f87 100644 --- a/client.go +++ b/client.go @@ -42,6 +42,12 @@ var ( // is too soon for us. ErrExpiryTooFar = errors.New("swap expiry too far") + // ErrSweepConfTargetTooFar is returned when the client proposes a + // confirmation target to sweep the on-chain HTLC of a Loop Out that is + // beyond the expiration height proposed by the server. + ErrSweepConfTargetTooFar = errors.New("sweep confirmation target is " + + "beyond swap expiration height") + serverRPCTimeout = 30 * time.Second republishDelay = 10 * time.Second diff --git a/loopout.go b/loopout.go index aec79c0..64c17f1 100644 --- a/loopout.go +++ b/loopout.go @@ -25,6 +25,13 @@ var ( // DefaultSweepConfTarget is the default confirmation target we'll use // when sweeping on-chain HTLCs. DefaultSweepConfTarget int32 = 6 + + // DefaultSweepConfTargetDelta is the delta of blocks from a Loop Out + // swap's expiration height at which we begin to use the default sweep + // confirmation target. + // + // TODO(wilmer): tune? + DefaultSweepConfTargetDelta int32 = DefaultSweepConfTarget * 2 ) // loopOutSwap contains all the in-memory state related to a pending loop out @@ -581,22 +588,29 @@ func (s *loopOutSwap) sweep(ctx context.Context, htlcValue btcutil.Amount) error { witnessFunc := func(sig []byte) (wire.TxWitness, error) { - return s.htlc.GenSuccessWitness( - sig, s.Preimage, - ) + return s.htlc.GenSuccessWitness(sig, s.Preimage) } - // Calculate sweep tx fee + // Calculate the transaction fee based on the confirmation target + // required to sweep the HTLC before the timeout. We'll use the + // confirmation target provided by the client unless we've come too + // close to the expiration height, in which case we'll use the default + // if it is better than what the client provided. + confTarget := s.SweepConfTarget + if s.CltvExpiry-s.height >= DefaultSweepConfTargetDelta && + confTarget > DefaultSweepConfTarget { + confTarget = DefaultSweepConfTarget + } fee, err := s.sweeper.GetSweepFee( - ctx, s.htlc.AddSuccessToEstimator, - s.SweepConfTarget, + ctx, s.htlc.AddSuccessToEstimator, confTarget, ) if err != nil { return err } + // Ensure it doesn't exceed our maximum fee allowed. if fee > s.MaxMinerFee { - s.log.Warnf("Required miner fee %v exceeds max of %v", + s.log.Warnf("Required fee %v exceeds max miner fee of %v", fee, s.MaxMinerFee) if s.state == loopdb.StatePreimageRevealed { @@ -612,8 +626,7 @@ func (s *loopOutSwap) sweep(ctx context.Context, // Create sweep tx. sweepTx, err := s.sweeper.CreateSweepTx( - ctx, s.height, s.htlc, htlcOutpoint, - s.ReceiverKey, witnessFunc, + ctx, s.height, s.htlc, htlcOutpoint, s.ReceiverKey, witnessFunc, htlcValue, fee, s.DestAddr, ) if err != nil { @@ -690,5 +703,11 @@ func validateLoopOutContract(lnd *lndclient.LndServices, return ErrExpiryTooSoon } + // Ensure the client has provided a sweep confirmation target that does + // not exceed the height at which we revert back to using the default. + if height+request.SweepConfTarget >= response.expiry-DefaultSweepConfTargetDelta { + return ErrSweepConfTargetTooFar + } + return nil }