Skip to content

Commit 3b896ba

Browse files
committed
liquidity: add test for recurring budget
1 parent 5d6be01 commit 3b896ba

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed

liquidity/autoloop_test.go

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,211 @@ func TestAutoloopBothTypes(t *testing.T) {
966966
c.stop()
967967
}
968968

969+
// TestAutoLoopRecurringBudget tests that the autolooper will perform swaps that
970+
// respect the fee budget, and that it will refresh the budget based on the
971+
// defined refresh period.
972+
func TestAutoLoopRecurringBudget(t *testing.T) {
973+
defer test.Guard(t)()
974+
975+
var (
976+
channels = []lndclient.ChannelInfo{
977+
channel1, channel2,
978+
}
979+
980+
swapFeePPM uint64 = 1000
981+
routeFeePPM uint64 = 1000
982+
prepayFeePPM uint64 = 1000
983+
prepayAmount = btcutil.Amount(20000)
984+
maxMiner = btcutil.Amount(20000)
985+
986+
params = Parameters{
987+
Autoloop: true,
988+
AutoFeeBudget: 36000,
989+
AutoFeeRefreshPeriod: time.Hour * 3,
990+
MaxAutoInFlight: 2,
991+
FailureBackOff: time.Hour,
992+
SweepConfTarget: 10,
993+
FeeLimit: NewFeeCategoryLimit(
994+
swapFeePPM, routeFeePPM, prepayFeePPM, maxMiner,
995+
prepayAmount, 20000,
996+
),
997+
ChannelRules: map[lnwire.ShortChannelID]*SwapRule{
998+
chanID1: chanRule,
999+
chanID2: chanRule,
1000+
},
1001+
HtlcConfTarget: defaultHtlcConfTarget,
1002+
}
1003+
)
1004+
1005+
c := newAutoloopTestCtx(t, params, channels, testRestrictions)
1006+
c.start()
1007+
1008+
// Calculate our maximum allowed fees and create quotes that fall within
1009+
// our budget.
1010+
var (
1011+
amt = chan1Rec.Amount
1012+
1013+
maxSwapFee = ppmToSat(amt, swapFeePPM)
1014+
1015+
// Create a quote that is within our limits. We do not set miner
1016+
// fee because this value is not actually set by the server.
1017+
quote1 = &loop.LoopOutQuote{
1018+
SwapFee: maxSwapFee,
1019+
PrepayAmount: prepayAmount - 10,
1020+
MinerFee: maxMiner - 10,
1021+
}
1022+
1023+
quote2 = &loop.LoopOutQuote{
1024+
SwapFee: maxSwapFee,
1025+
PrepayAmount: prepayAmount - 20,
1026+
MinerFee: maxMiner - 10,
1027+
}
1028+
1029+
quoteRequest = &loop.LoopOutQuoteRequest{
1030+
Amount: amt,
1031+
SweepConfTarget: params.SweepConfTarget,
1032+
}
1033+
1034+
quotes1 = []quoteRequestResp{
1035+
{
1036+
request: quoteRequest,
1037+
quote: quote1,
1038+
},
1039+
{
1040+
request: quoteRequest,
1041+
quote: quote2,
1042+
},
1043+
}
1044+
1045+
quotes2 = []quoteRequestResp{
1046+
{
1047+
request: quoteRequest,
1048+
quote: quote2,
1049+
},
1050+
}
1051+
1052+
maxRouteFee = ppmToSat(amt, routeFeePPM)
1053+
1054+
chan1Swap = &loop.OutRequest{
1055+
Amount: amt,
1056+
MaxSwapRoutingFee: maxRouteFee,
1057+
MaxPrepayRoutingFee: ppmToSat(
1058+
quote1.PrepayAmount, prepayFeePPM,
1059+
),
1060+
MaxSwapFee: quote1.SwapFee,
1061+
MaxPrepayAmount: quote1.PrepayAmount,
1062+
MaxMinerFee: maxMiner,
1063+
SweepConfTarget: params.SweepConfTarget,
1064+
OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()},
1065+
Label: labels.AutoloopLabel(swap.TypeOut),
1066+
Initiator: autoloopSwapInitiator,
1067+
}
1068+
1069+
chan2Swap = &loop.OutRequest{
1070+
Amount: amt,
1071+
MaxSwapRoutingFee: maxRouteFee,
1072+
MaxPrepayRoutingFee: ppmToSat(
1073+
quote2.PrepayAmount, routeFeePPM,
1074+
),
1075+
MaxSwapFee: quote2.SwapFee,
1076+
MaxPrepayAmount: quote2.PrepayAmount,
1077+
MaxMinerFee: maxMiner,
1078+
SweepConfTarget: params.SweepConfTarget,
1079+
OutgoingChanSet: loopdb.ChannelSet{chanID2.ToUint64()},
1080+
Label: labels.AutoloopLabel(swap.TypeOut),
1081+
Initiator: autoloopSwapInitiator,
1082+
}
1083+
1084+
loopOuts1 = []loopOutRequestResp{
1085+
{
1086+
request: chan1Swap,
1087+
response: &loop.LoopOutSwapInfo{
1088+
SwapHash: lntypes.Hash{1},
1089+
},
1090+
},
1091+
}
1092+
1093+
loopOuts2 = []loopOutRequestResp{
1094+
{
1095+
request: chan2Swap,
1096+
response: &loop.LoopOutSwapInfo{
1097+
SwapHash: lntypes.Hash{1},
1098+
},
1099+
},
1100+
}
1101+
)
1102+
1103+
// Tick our autolooper with no existing swaps, we expect a loop out
1104+
// swap to be dispatched on first channel.
1105+
step := &autoloopStep{
1106+
minAmt: 1,
1107+
maxAmt: amt + 1,
1108+
quotesOut: quotes1,
1109+
expectedOut: loopOuts1,
1110+
}
1111+
c.autoloop(step)
1112+
1113+
existing := []*loopdb.LoopOut{
1114+
existingSwapFromRequest(
1115+
chan1Swap, testTime, []*loopdb.LoopEvent{
1116+
{
1117+
SwapStateData: loopdb.SwapStateData{
1118+
State: loopdb.StateInitiated,
1119+
},
1120+
Time: testTime,
1121+
},
1122+
},
1123+
),
1124+
}
1125+
1126+
step = &autoloopStep{
1127+
minAmt: 1,
1128+
maxAmt: amt + 1,
1129+
quotesOut: quotes2,
1130+
existingOut: existing,
1131+
expectedOut: nil,
1132+
}
1133+
// Tick again, we should expect no loop outs because our budget would be
1134+
// exceeded.
1135+
c.autoloop(step)
1136+
1137+
// Create the existing entry for the first swap, marking its last update
1138+
// with success and a specific timestamp.
1139+
existing2 := []*loopdb.LoopOut{
1140+
existingSwapFromRequest(
1141+
chan1Swap, testTime, []*loopdb.LoopEvent{
1142+
{
1143+
SwapStateData: loopdb.SwapStateData{
1144+
State: loopdb.StateSuccess,
1145+
},
1146+
Time: testTime,
1147+
},
1148+
},
1149+
),
1150+
}
1151+
1152+
// Apply the balance shifts on the channels in order to get the correct
1153+
// recommendations on next tick.
1154+
c.lnd.Channels[0].LocalBalance = 2500
1155+
c.lnd.Channels[0].RemoteBalance = 7500
1156+
1157+
// Advance time to the future, causing a budget refresh.
1158+
c.testClock.SetTime(testTime.Add(time.Hour * 25))
1159+
1160+
step = &autoloopStep{
1161+
minAmt: 1,
1162+
maxAmt: amt + 1,
1163+
quotesOut: quotes2,
1164+
existingOut: existing2,
1165+
expectedOut: loopOuts2,
1166+
}
1167+
1168+
// Tick again, we should expect a loop out to occur on the 2nd channel.
1169+
c.autoloop(step)
1170+
1171+
c.stop()
1172+
}
1173+
9691174
// existingSwapFromRequest is a helper function which returns the db
9701175
// representation of a loop out request with the event set provided.
9711176
func existingSwapFromRequest(request *loop.OutRequest, initTime time.Time,

0 commit comments

Comments
 (0)