Skip to content

Commit c067169

Browse files
committed
liquidity: add loop fee estimation and swap interface impl
Add an implementation of our swap interface which can be used for loop in, and fee estimation. For fee estimation, we always want to calculate worst case loop in fees, so that autoloop never goes over its budget. However, for loop in we can't estimate how much a timeout would cost, because we must sweep the output (can't set a limit like loop out), and fee estimation in a few hundred blocks (when we'd sweep the timeout) is totally unreliable. Instead, we use a high fee rate as our best-effort fee rate for the future. We can also be confident that that loop in swaps will succeed, since once the htlc is locked in, all that is required is for the server to sweep.
1 parent 3e7782e commit c067169

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

liquidity/liquidity.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

8692
var (

liquidity/loopin.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package liquidity
2+
3+
import (
4+
"github.com/btcsuite/btcutil"
5+
"github.com/lightninglabs/loop"
6+
"github.com/lightninglabs/loop/swap"
7+
"github.com/lightningnetwork/lnd/input"
8+
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
9+
"github.com/lightningnetwork/lnd/lnwire"
10+
"github.com/lightningnetwork/lnd/routing/route"
11+
)
12+
13+
// Compile time assertion that loop in suggestions satisfy our interface.
14+
var _ swapSuggestion = (*loopInSwapSuggestion)(nil)
15+
16+
type loopInSwapSuggestion struct {
17+
loop.LoopInRequest
18+
}
19+
20+
// amount returns the amount of the swap suggestion.
21+
func (l *loopInSwapSuggestion) amount() btcutil.Amount {
22+
return l.Amount
23+
}
24+
25+
// fees returns the highest fees that we could pay for the swap suggestion.
26+
func (l *loopInSwapSuggestion) fees() btcutil.Amount {
27+
return worstCaseInFees(
28+
l.MaxMinerFee, l.MaxSwapFee, defaultLoopInSweepFee,
29+
)
30+
}
31+
32+
// channels returns no channels for loop in swap suggestions because we do not
33+
// restrict loop in swaps by channel id.
34+
func (l *loopInSwapSuggestion) channels() []lnwire.ShortChannelID {
35+
return nil
36+
}
37+
38+
// peers returns the peer that a loop in swap is restricted to, if it is set.
39+
func (l *loopInSwapSuggestion) peers(_ map[uint64]route.Vertex) []route.Vertex {
40+
if l.LastHop == nil {
41+
return nil
42+
}
43+
44+
return []route.Vertex{
45+
*l.LastHop,
46+
}
47+
}
48+
49+
// worstCaseInFees returns the largest possible fees for a loop in swap.
50+
func worstCaseInFees(maxMinerFee, swapFee btcutil.Amount,
51+
sweepEst chainfee.SatPerKWeight) btcutil.Amount {
52+
53+
failureFee := maxMinerFee + loopInSweepFee(sweepEst)
54+
successFee := maxMinerFee + swapFee
55+
56+
if failureFee > successFee {
57+
return failureFee
58+
}
59+
60+
return successFee
61+
}
62+
63+
// loopInSweepFee provides an estimated fee for our sweep transaction, based
64+
// on the fee rate provided. We can calculate our fees for htlcv2 and p2wkh
65+
// timeout addresses because automated loop ins will be handled entirely by the
66+
// client, so we know what types will be used.
67+
func loopInSweepFee(fee chainfee.SatPerKWeight) btcutil.Amount {
68+
var estimator input.TxWeightEstimator
69+
70+
// We sweep loop in swaps to wpkh addresses provided by lnd.
71+
estimator.AddP2WKHOutput()
72+
73+
// Create a htlcv2, which is what all autoloops will use, so that we
74+
// can get our maximum timeout witness size.
75+
htlc := swap.HtlcScriptV2{}
76+
maxSize := htlc.MaxTimeoutWitnessSize()
77+
78+
estimator.AddWitnessInput(maxSize)
79+
weight := int64(estimator.Weight())
80+
81+
return fee.FeeForWeight(weight)
82+
}

0 commit comments

Comments
 (0)