liquidity: add test for recurring budget

pull/551/head
George Tsagkarelis 1 year ago
parent 5d6be011f9
commit 3b896babc6
No known key found for this signature in database
GPG Key ID: 0807D1013F48208A

@ -966,6 +966,211 @@ func TestAutoloopBothTypes(t *testing.T) {
c.stop()
}
// TestAutoLoopRecurringBudget tests that the autolooper will perform swaps that
// respect the fee budget, and that it will refresh the budget based on the
// defined refresh period.
func TestAutoLoopRecurringBudget(t *testing.T) {
defer test.Guard(t)()
var (
channels = []lndclient.ChannelInfo{
channel1, channel2,
}
swapFeePPM uint64 = 1000
routeFeePPM uint64 = 1000
prepayFeePPM uint64 = 1000
prepayAmount = btcutil.Amount(20000)
maxMiner = btcutil.Amount(20000)
params = Parameters{
Autoloop: true,
AutoFeeBudget: 36000,
AutoFeeRefreshPeriod: time.Hour * 3,
MaxAutoInFlight: 2,
FailureBackOff: time.Hour,
SweepConfTarget: 10,
FeeLimit: NewFeeCategoryLimit(
swapFeePPM, routeFeePPM, prepayFeePPM, maxMiner,
prepayAmount, 20000,
),
ChannelRules: map[lnwire.ShortChannelID]*SwapRule{
chanID1: chanRule,
chanID2: chanRule,
},
HtlcConfTarget: defaultHtlcConfTarget,
}
)
c := newAutoloopTestCtx(t, params, channels, testRestrictions)
c.start()
// Calculate our maximum allowed fees and create quotes that fall within
// our budget.
var (
amt = chan1Rec.Amount
maxSwapFee = ppmToSat(amt, swapFeePPM)
// Create a quote that is within our limits. We do not set miner
// fee because this value is not actually set by the server.
quote1 = &loop.LoopOutQuote{
SwapFee: maxSwapFee,
PrepayAmount: prepayAmount - 10,
MinerFee: maxMiner - 10,
}
quote2 = &loop.LoopOutQuote{
SwapFee: maxSwapFee,
PrepayAmount: prepayAmount - 20,
MinerFee: maxMiner - 10,
}
quoteRequest = &loop.LoopOutQuoteRequest{
Amount: amt,
SweepConfTarget: params.SweepConfTarget,
}
quotes1 = []quoteRequestResp{
{
request: quoteRequest,
quote: quote1,
},
{
request: quoteRequest,
quote: quote2,
},
}
quotes2 = []quoteRequestResp{
{
request: quoteRequest,
quote: quote2,
},
}
maxRouteFee = ppmToSat(amt, routeFeePPM)
chan1Swap = &loop.OutRequest{
Amount: amt,
MaxSwapRoutingFee: maxRouteFee,
MaxPrepayRoutingFee: ppmToSat(
quote1.PrepayAmount, prepayFeePPM,
),
MaxSwapFee: quote1.SwapFee,
MaxPrepayAmount: quote1.PrepayAmount,
MaxMinerFee: maxMiner,
SweepConfTarget: params.SweepConfTarget,
OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()},
Label: labels.AutoloopLabel(swap.TypeOut),
Initiator: autoloopSwapInitiator,
}
chan2Swap = &loop.OutRequest{
Amount: amt,
MaxSwapRoutingFee: maxRouteFee,
MaxPrepayRoutingFee: ppmToSat(
quote2.PrepayAmount, routeFeePPM,
),
MaxSwapFee: quote2.SwapFee,
MaxPrepayAmount: quote2.PrepayAmount,
MaxMinerFee: maxMiner,
SweepConfTarget: params.SweepConfTarget,
OutgoingChanSet: loopdb.ChannelSet{chanID2.ToUint64()},
Label: labels.AutoloopLabel(swap.TypeOut),
Initiator: autoloopSwapInitiator,
}
loopOuts1 = []loopOutRequestResp{
{
request: chan1Swap,
response: &loop.LoopOutSwapInfo{
SwapHash: lntypes.Hash{1},
},
},
}
loopOuts2 = []loopOutRequestResp{
{
request: chan2Swap,
response: &loop.LoopOutSwapInfo{
SwapHash: lntypes.Hash{1},
},
},
}
)
// Tick our autolooper with no existing swaps, we expect a loop out
// swap to be dispatched on first channel.
step := &autoloopStep{
minAmt: 1,
maxAmt: amt + 1,
quotesOut: quotes1,
expectedOut: loopOuts1,
}
c.autoloop(step)
existing := []*loopdb.LoopOut{
existingSwapFromRequest(
chan1Swap, testTime, []*loopdb.LoopEvent{
{
SwapStateData: loopdb.SwapStateData{
State: loopdb.StateInitiated,
},
Time: testTime,
},
},
),
}
step = &autoloopStep{
minAmt: 1,
maxAmt: amt + 1,
quotesOut: quotes2,
existingOut: existing,
expectedOut: nil,
}
// Tick again, we should expect no loop outs because our budget would be
// exceeded.
c.autoloop(step)
// Create the existing entry for the first swap, marking its last update
// with success and a specific timestamp.
existing2 := []*loopdb.LoopOut{
existingSwapFromRequest(
chan1Swap, testTime, []*loopdb.LoopEvent{
{
SwapStateData: loopdb.SwapStateData{
State: loopdb.StateSuccess,
},
Time: testTime,
},
},
),
}
// Apply the balance shifts on the channels in order to get the correct
// recommendations on next tick.
c.lnd.Channels[0].LocalBalance = 2500
c.lnd.Channels[0].RemoteBalance = 7500
// Advance time to the future, causing a budget refresh.
c.testClock.SetTime(testTime.Add(time.Hour * 25))
step = &autoloopStep{
minAmt: 1,
maxAmt: amt + 1,
quotesOut: quotes2,
existingOut: existing2,
expectedOut: loopOuts2,
}
// Tick again, we should expect a loop out to occur on the 2nd channel.
c.autoloop(step)
c.stop()
}
// existingSwapFromRequest is a helper function which returns the db
// representation of a loop out request with the event set provided.
func existingSwapFromRequest(request *loop.OutRequest, initTime time.Time,

Loading…
Cancel
Save