2
0
mirror of https://github.com/lightninglabs/loop synced 2024-11-13 13:10:30 +00:00
loop/liquidity/loopin.go
carla c067169e6f
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.
2021-11-30 13:18:30 +02:00

83 lines
2.4 KiB
Go

package liquidity
import (
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/swap"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
// Compile time assertion that loop in suggestions satisfy our interface.
var _ swapSuggestion = (*loopInSwapSuggestion)(nil)
type loopInSwapSuggestion struct {
loop.LoopInRequest
}
// amount returns the amount of the swap suggestion.
func (l *loopInSwapSuggestion) amount() btcutil.Amount {
return l.Amount
}
// fees returns the highest fees that we could pay for the swap suggestion.
func (l *loopInSwapSuggestion) fees() btcutil.Amount {
return worstCaseInFees(
l.MaxMinerFee, l.MaxSwapFee, defaultLoopInSweepFee,
)
}
// channels returns no channels for loop in swap suggestions because we do not
// restrict loop in swaps by channel id.
func (l *loopInSwapSuggestion) channels() []lnwire.ShortChannelID {
return nil
}
// peers returns the peer that a loop in swap is restricted to, if it is set.
func (l *loopInSwapSuggestion) peers(_ map[uint64]route.Vertex) []route.Vertex {
if l.LastHop == nil {
return nil
}
return []route.Vertex{
*l.LastHop,
}
}
// worstCaseInFees returns the largest possible fees for a loop in swap.
func worstCaseInFees(maxMinerFee, swapFee btcutil.Amount,
sweepEst chainfee.SatPerKWeight) btcutil.Amount {
failureFee := maxMinerFee + loopInSweepFee(sweepEst)
successFee := maxMinerFee + swapFee
if failureFee > successFee {
return failureFee
}
return successFee
}
// loopInSweepFee provides an estimated fee for our sweep transaction, based
// on the fee rate provided. We can calculate our fees for htlcv2 and p2wkh
// timeout addresses because automated loop ins will be handled entirely by the
// client, so we know what types will be used.
func loopInSweepFee(fee chainfee.SatPerKWeight) btcutil.Amount {
var estimator input.TxWeightEstimator
// We sweep loop in swaps to wpkh addresses provided by lnd.
estimator.AddP2WKHOutput()
// Create a htlcv2, which is what all autoloops will use, so that we
// can get our maximum timeout witness size.
htlc := swap.HtlcScriptV2{}
maxSize := htlc.MaxTimeoutWitnessSize()
estimator.AddWitnessInput(maxSize)
weight := int64(estimator.Weight())
return fee.FeeForWeight(weight)
}