Merge pull request #581 from GeorgeTsagk/fee-percentage-miner

liquidity: differentiate autoloop expected vs max miner fees
pull/587/head
George Tsagkarelis 1 year ago committed by GitHub
commit 0a19485409
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -38,10 +38,14 @@ const (
// sweep fees, (750 * 4 /1000 = 3 sat/vByte). // sweep fees, (750 * 4 /1000 = 3 sat/vByte).
defaultSweepFeeRateLimit = chainfee.SatPerKWeight(750) defaultSweepFeeRateLimit = chainfee.SatPerKWeight(750)
// minerMultiplier is a multiplier we use to scale our miner fee to // minerMultiplier is a multiplier we use to predict the average chain
// ensure that we will still be able to complete our swap in the case // costs towards miner fees.
// of a severe fee spike. minerMultiplier = 2
minerMultiplier = 100
// maxMinerMultiplier is the maximum multiplier we use to scale our
// miner fee to ensure that we will still be able to complete our swap
// in the case of a severe fee spike.
maxMinerMultiplier = 50
// defaultFeePPM is the default percentage of swap amount that we // defaultFeePPM is the default percentage of swap amount that we
// allocate to fees, 2%. // allocate to fees, 2%.
@ -341,6 +345,12 @@ func (f *FeePortion) loopOutLimits(swapAmt btcutil.Amount,
prepay, route, miner := f.loopOutFees(swapAmt, quote) prepay, route, miner := f.loopOutFees(swapAmt, quote)
// Before checking our fees against our budget we remove the large
// multiplier from the miner fees. We do this because we want to
// consider the average case for our budget calculations and not the
// severe edge-case miner fees.
miner = miner / maxMinerMultiplier
// Calculate the worst case fees that we could pay for this swap, // Calculate the worst case fees that we could pay for this swap,
// ensuring that we are within our fee limit even if the swap fails. // ensuring that we are within our fee limit even if the swap fails.
fees := worstCaseOutFees( fees := worstCaseOutFees(
@ -370,6 +380,8 @@ func (f *FeePortion) loopOutFees(amount btcutil.Amount,
// amounts provided by the quote to get the total available for // amounts provided by the quote to get the total available for
// off-chain fees. // off-chain fees.
feeLimit := ppmToSat(amount, f.PartsPerMillion) feeLimit := ppmToSat(amount, f.PartsPerMillion)
// Apply the small miner multiplier for the fee budget calculations.
minerFee := scaleMinerFee(quote.MinerFee) minerFee := scaleMinerFee(quote.MinerFee)
available := feeLimit - minerFee - quote.SwapFee available := feeLimit - minerFee - quote.SwapFee
@ -378,6 +390,9 @@ func (f *FeePortion) loopOutFees(amount btcutil.Amount,
available, quote.PrepayAmount, amount, available, quote.PrepayAmount, amount,
) )
// Apply the big miner multiplier to get the worst case miner fees.
minerFee = scaleMaxMinerFee(minerFee)
return prepayMaxFee, routeMaxFee, minerFee return prepayMaxFee, routeMaxFee, minerFee
} }
@ -394,11 +409,20 @@ func splitOffChain(available, prepayAmt,
return prepayMaxFee, routeMaxFee return prepayMaxFee, routeMaxFee
} }
// scaleMinerFee scales our miner fee by our constant multiplier. // scaleMinerFee scales our miner fee by a smaller multiplier. This scale does
// not represent the worst-case maximum miner fees, but the average expected
// fees.
func scaleMinerFee(estimate btcutil.Amount) btcutil.Amount { func scaleMinerFee(estimate btcutil.Amount) btcutil.Amount {
return estimate * btcutil.Amount(minerMultiplier) return estimate * btcutil.Amount(minerMultiplier)
} }
// scaleMaxMinerFee scales our miner fee by a big multiplier. The returned value
// represents the maximum amount that we consider spending for miner fees in
// worst-case scenarios (fee-spikes).
func scaleMaxMinerFee(estimate btcutil.Amount) btcutil.Amount {
return estimate * btcutil.Amount(maxMinerMultiplier)
}
func (f *FeePortion) loopInLimits(amount btcutil.Amount, func (f *FeePortion) loopInLimits(amount btcutil.Amount,
quote *loop.LoopInQuote) error { quote *loop.LoopInQuote) error {

@ -73,11 +73,13 @@ var (
OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()}, OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()},
MaxPrepayRoutingFee: prepayFee, MaxPrepayRoutingFee: prepayFee,
MaxSwapRoutingFee: routingFee, MaxSwapRoutingFee: routingFee,
MaxMinerFee: scaleMinerFee(testQuote.MinerFee), MaxMinerFee: scaleMaxMinerFee(
MaxSwapFee: testQuote.SwapFee, scaleMinerFee(testQuote.MinerFee),
MaxPrepayAmount: testQuote.PrepayAmount, ),
SweepConfTarget: defaultConfTarget, MaxSwapFee: testQuote.SwapFee,
Initiator: autoloopSwapInitiator, MaxPrepayAmount: testQuote.PrepayAmount,
SweepConfTarget: defaultConfTarget,
Initiator: autoloopSwapInitiator,
} }
// chan2Rec is the suggested swap for channel 2 when we use chanRule. // chan2Rec is the suggested swap for channel 2 when we use chanRule.
@ -86,11 +88,13 @@ var (
OutgoingChanSet: loopdb.ChannelSet{chanID2.ToUint64()}, OutgoingChanSet: loopdb.ChannelSet{chanID2.ToUint64()},
MaxPrepayRoutingFee: prepayFee, MaxPrepayRoutingFee: prepayFee,
MaxSwapRoutingFee: routingFee, MaxSwapRoutingFee: routingFee,
MaxMinerFee: scaleMinerFee(testQuote.MinerFee), MaxMinerFee: scaleMaxMinerFee(
MaxPrepayAmount: testQuote.PrepayAmount, scaleMinerFee(testQuote.MinerFee),
MaxSwapFee: testQuote.SwapFee, ),
SweepConfTarget: defaultConfTarget, MaxPrepayAmount: testQuote.PrepayAmount,
Initiator: autoloopSwapInitiator, MaxSwapFee: testQuote.SwapFee,
SweepConfTarget: defaultConfTarget,
Initiator: autoloopSwapInitiator,
} }
// chan1Out is a contract that uses channel 1, used to represent on // chan1Out is a contract that uses channel 1, used to represent on
@ -771,11 +775,13 @@ func TestSuggestSwaps(t *testing.T) {
}, },
MaxPrepayRoutingFee: prepay, MaxPrepayRoutingFee: prepay,
MaxSwapRoutingFee: routing, MaxSwapRoutingFee: routing,
MaxMinerFee: scaleMinerFee(testQuote.MinerFee), MaxMinerFee: scaleMaxMinerFee(
MaxSwapFee: testQuote.SwapFee, scaleMinerFee(testQuote.MinerFee),
MaxPrepayAmount: testQuote.PrepayAmount, ),
SweepConfTarget: defaultConfTarget, MaxSwapFee: testQuote.SwapFee,
Initiator: autoloopSwapInitiator, MaxPrepayAmount: testQuote.PrepayAmount,
SweepConfTarget: defaultConfTarget,
Initiator: autoloopSwapInitiator,
}, },
}, },
DisqualifiedChans: noneDisqualified, DisqualifiedChans: noneDisqualified,
@ -1330,11 +1336,13 @@ func TestSizeRestrictions(t *testing.T) {
OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()}, OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()},
MaxPrepayRoutingFee: prepay, MaxPrepayRoutingFee: prepay,
MaxSwapRoutingFee: routing, MaxSwapRoutingFee: routing,
MaxMinerFee: scaleMinerFee(testQuote.MinerFee), MaxMinerFee: scaleMaxMinerFee(
MaxSwapFee: testQuote.SwapFee, scaleMinerFee(testQuote.MinerFee),
MaxPrepayAmount: testQuote.PrepayAmount, ),
SweepConfTarget: defaultConfTarget, MaxSwapFee: testQuote.SwapFee,
Initiator: autoloopSwapInitiator, MaxPrepayAmount: testQuote.PrepayAmount,
SweepConfTarget: defaultConfTarget,
Initiator: autoloopSwapInitiator,
} }
) )
@ -1487,7 +1495,9 @@ func TestFeePercentage(t *testing.T) {
rec = loop.OutRequest{ rec = loop.OutRequest{
Amount: 7500, Amount: 7500,
OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()}, OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()},
MaxMinerFee: scaleMinerFee(okQuote.MinerFee), MaxMinerFee: scaleMaxMinerFee(
scaleMinerFee(testQuote.MinerFee),
),
MaxSwapFee: okQuote.SwapFee, MaxSwapFee: okQuote.SwapFee,
MaxPrepayAmount: okQuote.PrepayAmount, MaxPrepayAmount: okQuote.PrepayAmount,
SweepConfTarget: defaultConfTarget, SweepConfTarget: defaultConfTarget,
@ -1555,7 +1565,7 @@ func TestFeePercentage(t *testing.T) {
quote: &loop.LoopOutQuote{ quote: &loop.LoopOutQuote{
SwapFee: 60, SwapFee: 60,
PrepayAmount: 30, PrepayAmount: 30,
MinerFee: 1, MinerFee: 50,
}, },
suggestions: &Suggestions{ suggestions: &Suggestions{
DisqualifiedChans: map[lnwire.ShortChannelID]Reason{ DisqualifiedChans: map[lnwire.ShortChannelID]Reason{
@ -1650,7 +1660,9 @@ func TestBudgetWithLoopin(t *testing.T) {
rec = loop.OutRequest{ rec = loop.OutRequest{
Amount: 7500, Amount: 7500,
OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()}, OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()},
MaxMinerFee: scaleMinerFee(okQuote.MinerFee), MaxMinerFee: scaleMaxMinerFee(
scaleMinerFee(testQuote.MinerFee),
),
MaxSwapFee: okQuote.SwapFee, MaxSwapFee: okQuote.SwapFee,
MaxPrepayAmount: okQuote.PrepayAmount, MaxPrepayAmount: okQuote.PrepayAmount,
SweepConfTarget: defaultConfTarget, SweepConfTarget: defaultConfTarget,

Loading…
Cancel
Save