mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
liquidity/test: add autoloop in tests
This commit is contained in:
parent
5280721636
commit
66d16f52a7
1 changed files with 364 additions and 0 deletions
|
|
@ -471,6 +471,349 @@ func TestCompositeRules(t *testing.T) {
|
|||
c.stop()
|
||||
}
|
||||
|
||||
// TestAutoLoopInEnabled tests dispatch of autoloop in swaps.
|
||||
func TestAutoLoopInEnabled(t *testing.T) {
|
||||
defer test.Guard(t)()
|
||||
|
||||
var (
|
||||
chan1 = lndclient.ChannelInfo{
|
||||
ChannelID: chanID1.ToUint64(),
|
||||
PubKeyBytes: peer1,
|
||||
Capacity: 100000,
|
||||
RemoteBalance: 100000,
|
||||
LocalBalance: 0,
|
||||
}
|
||||
|
||||
chan2 = lndclient.ChannelInfo{
|
||||
ChannelID: chanID2.ToUint64(),
|
||||
PubKeyBytes: peer2,
|
||||
Capacity: 200000,
|
||||
RemoteBalance: 200000,
|
||||
LocalBalance: 0,
|
||||
}
|
||||
|
||||
channels = []lndclient.ChannelInfo{
|
||||
chan1, chan2,
|
||||
}
|
||||
|
||||
// Create a rule which will loop in, with no inbound liquidity
|
||||
// reserve.
|
||||
rule = &SwapRule{
|
||||
ThresholdRule: NewThresholdRule(0, 60),
|
||||
Type: swap.TypeIn,
|
||||
}
|
||||
|
||||
// Under these rules, we'll have the following recommended
|
||||
// swaps:
|
||||
peer1ExpectedAmt btcutil.Amount = 80000
|
||||
peer2ExpectedAmt btcutil.Amount = 160000
|
||||
|
||||
// Set our per-swap budget to 5% of swap amount.
|
||||
swapFeePPM uint64 = 50000
|
||||
|
||||
htlcConfTarget int32 = 10
|
||||
|
||||
// Calculate the maximum amount we'll pay for each swap and
|
||||
// set our budget to be able to accommodate both.
|
||||
peer1MaxFee = ppmToSat(peer1ExpectedAmt, swapFeePPM)
|
||||
peer2MaxFee = ppmToSat(peer2ExpectedAmt, swapFeePPM)
|
||||
|
||||
params = Parameters{
|
||||
Autoloop: true,
|
||||
AutoFeeBudget: peer1MaxFee + peer2MaxFee + 1,
|
||||
AutoFeeStartDate: testTime,
|
||||
MaxAutoInFlight: 2,
|
||||
FailureBackOff: time.Hour,
|
||||
FeeLimit: NewFeePortion(swapFeePPM),
|
||||
ChannelRules: make(map[lnwire.ShortChannelID]*SwapRule),
|
||||
PeerRules: map[route.Vertex]*SwapRule{
|
||||
peer1: rule,
|
||||
peer2: rule,
|
||||
},
|
||||
HtlcConfTarget: htlcConfTarget,
|
||||
SweepConfTarget: loop.DefaultSweepConfTarget,
|
||||
}
|
||||
)
|
||||
c := newAutoloopTestCtx(t, params, channels, testRestrictions)
|
||||
c.start()
|
||||
|
||||
// Calculate our maximum allowed fees and create quotes that fall within
|
||||
// our budget.
|
||||
var (
|
||||
quote1 = &loop.LoopInQuote{
|
||||
SwapFee: peer1MaxFee / 4,
|
||||
MinerFee: peer1MaxFee / 8,
|
||||
}
|
||||
|
||||
quote2Unaffordable = &loop.LoopInQuote{
|
||||
SwapFee: peer2MaxFee * 2,
|
||||
MinerFee: peer2MaxFee * 2,
|
||||
}
|
||||
|
||||
quoteRequest1 = &loop.LoopInQuoteRequest{
|
||||
Amount: peer1ExpectedAmt,
|
||||
HtlcConfTarget: htlcConfTarget,
|
||||
LastHop: &peer1,
|
||||
}
|
||||
|
||||
quoteRequest2 = &loop.LoopInQuoteRequest{
|
||||
Amount: peer2ExpectedAmt,
|
||||
HtlcConfTarget: htlcConfTarget,
|
||||
LastHop: &peer2,
|
||||
}
|
||||
|
||||
peer1Swap = &loop.LoopInRequest{
|
||||
Amount: peer1ExpectedAmt,
|
||||
MaxSwapFee: quote1.SwapFee,
|
||||
MaxMinerFee: quote1.MinerFee,
|
||||
HtlcConfTarget: htlcConfTarget,
|
||||
LastHop: &peer1,
|
||||
ExternalHtlc: false,
|
||||
Label: labels.AutoloopLabel(swap.TypeIn),
|
||||
Initiator: autoloopSwapInitiator,
|
||||
}
|
||||
)
|
||||
|
||||
// Tick our autolooper with no existing swaps. Both of our peers
|
||||
// require swaps, but one of our peer's quotes is too expensive.
|
||||
step := &autoloopStep{
|
||||
minAmt: 1,
|
||||
maxAmt: peer2ExpectedAmt + 1,
|
||||
quotesIn: []quoteInRequestResp{
|
||||
{
|
||||
request: quoteRequest1,
|
||||
quote: quote1,
|
||||
},
|
||||
{
|
||||
request: quoteRequest2,
|
||||
quote: quote2Unaffordable,
|
||||
},
|
||||
},
|
||||
expectedIn: []loopInRequestResp{
|
||||
{
|
||||
request: peer1Swap,
|
||||
response: &loop.LoopInSwapInfo{
|
||||
SwapHash: lntypes.Hash{1},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
c.autoloop(step)
|
||||
|
||||
// Now, we tick again with our first swap in progress. This time, we
|
||||
// provide a quote for our second swap which is more affordable, so we
|
||||
// expect it to be dispatched.
|
||||
|
||||
var (
|
||||
quote2Affordable = &loop.LoopInQuote{
|
||||
SwapFee: peer2MaxFee / 8,
|
||||
MinerFee: peer2MaxFee / 2,
|
||||
}
|
||||
|
||||
peer2Swap = &loop.LoopInRequest{
|
||||
Amount: peer2ExpectedAmt,
|
||||
MaxSwapFee: quote2Affordable.SwapFee,
|
||||
MaxMinerFee: quote2Affordable.MinerFee,
|
||||
HtlcConfTarget: htlcConfTarget,
|
||||
LastHop: &peer2,
|
||||
ExternalHtlc: false,
|
||||
Label: labels.AutoloopLabel(swap.TypeIn),
|
||||
Initiator: autoloopSwapInitiator,
|
||||
}
|
||||
|
||||
existing = []*loopdb.LoopIn{
|
||||
existingInFromRequest(peer1Swap, testTime, nil),
|
||||
}
|
||||
)
|
||||
|
||||
step = &autoloopStep{
|
||||
minAmt: 1,
|
||||
maxAmt: peer2ExpectedAmt + 1,
|
||||
quotesIn: []quoteInRequestResp{
|
||||
{
|
||||
request: quoteRequest2,
|
||||
quote: quote2Affordable,
|
||||
},
|
||||
},
|
||||
existingIn: existing,
|
||||
expectedIn: []loopInRequestResp{
|
||||
{
|
||||
request: peer2Swap,
|
||||
response: &loop.LoopInSwapInfo{
|
||||
SwapHash: lntypes.Hash{2},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
c.autoloop(step)
|
||||
|
||||
c.stop()
|
||||
}
|
||||
|
||||
// TestAutoloopBothTypes tests dispatching of a loop out and loop in swap at the
|
||||
// same time.
|
||||
func TestAutoloopBothTypes(t *testing.T) {
|
||||
defer test.Guard(t)()
|
||||
|
||||
var (
|
||||
chan1 = lndclient.ChannelInfo{
|
||||
ChannelID: chanID1.ToUint64(),
|
||||
PubKeyBytes: peer1,
|
||||
Capacity: 1000000,
|
||||
LocalBalance: 1000000,
|
||||
}
|
||||
chan2 = lndclient.ChannelInfo{
|
||||
ChannelID: chanID2.ToUint64(),
|
||||
PubKeyBytes: peer2,
|
||||
Capacity: 200000,
|
||||
RemoteBalance: 200000,
|
||||
LocalBalance: 0,
|
||||
}
|
||||
|
||||
channels = []lndclient.ChannelInfo{
|
||||
chan1, chan2,
|
||||
}
|
||||
|
||||
// Create a rule which will loop out, with no outbound liquidity
|
||||
// reserve.
|
||||
outRule = &SwapRule{
|
||||
ThresholdRule: NewThresholdRule(40, 0),
|
||||
Type: swap.TypeOut,
|
||||
}
|
||||
|
||||
// Create a rule which will loop in, with no inbound liquidity
|
||||
// reserve.
|
||||
inRule = &SwapRule{
|
||||
ThresholdRule: NewThresholdRule(0, 60),
|
||||
Type: swap.TypeIn,
|
||||
}
|
||||
|
||||
// Under this rule, we expect a loop in swap.
|
||||
loopOutAmt btcutil.Amount = 700000
|
||||
loopInAmount btcutil.Amount = 160000
|
||||
|
||||
// Set our per-swap budget to 5% of swap amount.
|
||||
swapFeePPM uint64 = 50000
|
||||
|
||||
htlcConfTarget int32 = 10
|
||||
|
||||
// Calculate the maximum amount we'll pay for our loop in.
|
||||
loopOutMaxFee = ppmToSat(loopOutAmt, swapFeePPM)
|
||||
loopInMaxFee = ppmToSat(loopInAmount, swapFeePPM)
|
||||
|
||||
params = Parameters{
|
||||
Autoloop: true,
|
||||
AutoFeeBudget: loopOutMaxFee + loopInMaxFee + 1,
|
||||
AutoFeeStartDate: testTime,
|
||||
MaxAutoInFlight: 2,
|
||||
FailureBackOff: time.Hour,
|
||||
FeeLimit: NewFeePortion(swapFeePPM),
|
||||
ChannelRules: map[lnwire.ShortChannelID]*SwapRule{
|
||||
chanID1: outRule,
|
||||
},
|
||||
PeerRules: map[route.Vertex]*SwapRule{
|
||||
peer2: inRule,
|
||||
},
|
||||
HtlcConfTarget: htlcConfTarget,
|
||||
SweepConfTarget: loop.DefaultSweepConfTarget,
|
||||
}
|
||||
)
|
||||
c := newAutoloopTestCtx(t, params, channels, testRestrictions)
|
||||
c.start()
|
||||
|
||||
// Calculate our maximum allowed fees and create quotes that fall within
|
||||
// our budget.
|
||||
var (
|
||||
loopOutQuote = &loop.LoopOutQuote{
|
||||
SwapFee: loopOutMaxFee / 4,
|
||||
PrepayAmount: loopOutMaxFee / 4,
|
||||
}
|
||||
|
||||
loopOutQuoteReq = &loop.LoopOutQuoteRequest{
|
||||
Amount: loopOutAmt,
|
||||
SweepConfTarget: params.SweepConfTarget,
|
||||
SwapPublicationDeadline: testTime,
|
||||
}
|
||||
|
||||
prepayMaxFee, routeMaxFee,
|
||||
minerFee = params.FeeLimit.loopOutFees(
|
||||
loopOutAmt, loopOutQuote,
|
||||
)
|
||||
|
||||
loopOutSwap = &loop.OutRequest{
|
||||
Amount: loopOutAmt,
|
||||
MaxSwapRoutingFee: routeMaxFee,
|
||||
MaxPrepayRoutingFee: prepayMaxFee,
|
||||
MaxSwapFee: loopOutQuote.SwapFee,
|
||||
MaxPrepayAmount: loopOutQuote.PrepayAmount,
|
||||
MaxMinerFee: minerFee,
|
||||
SweepConfTarget: params.SweepConfTarget,
|
||||
OutgoingChanSet: loopdb.ChannelSet{
|
||||
chanID1.ToUint64(),
|
||||
},
|
||||
Label: labels.AutoloopLabel(swap.TypeOut),
|
||||
Initiator: autoloopSwapInitiator,
|
||||
}
|
||||
|
||||
loopinQuote = &loop.LoopInQuote{
|
||||
SwapFee: loopInMaxFee / 4,
|
||||
MinerFee: loopInMaxFee / 8,
|
||||
}
|
||||
|
||||
loopInQuoteReq = &loop.LoopInQuoteRequest{
|
||||
Amount: loopInAmount,
|
||||
HtlcConfTarget: htlcConfTarget,
|
||||
LastHop: &peer2,
|
||||
}
|
||||
|
||||
loopInSwap = &loop.LoopInRequest{
|
||||
Amount: loopInAmount,
|
||||
MaxSwapFee: loopinQuote.SwapFee,
|
||||
MaxMinerFee: loopinQuote.MinerFee,
|
||||
HtlcConfTarget: htlcConfTarget,
|
||||
LastHop: &peer2,
|
||||
ExternalHtlc: false,
|
||||
Label: labels.AutoloopLabel(swap.TypeIn),
|
||||
Initiator: autoloopSwapInitiator,
|
||||
}
|
||||
)
|
||||
|
||||
step := &autoloopStep{
|
||||
minAmt: 1,
|
||||
maxAmt: loopOutAmt + 1,
|
||||
quotesOut: []quoteRequestResp{
|
||||
{
|
||||
request: loopOutQuoteReq,
|
||||
quote: loopOutQuote,
|
||||
},
|
||||
},
|
||||
quotesIn: []quoteInRequestResp{
|
||||
{
|
||||
request: loopInQuoteReq,
|
||||
quote: loopinQuote,
|
||||
},
|
||||
},
|
||||
expectedOut: []loopOutRequestResp{
|
||||
{
|
||||
request: loopOutSwap,
|
||||
response: &loop.LoopOutSwapInfo{
|
||||
SwapHash: lntypes.Hash{1},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedIn: []loopInRequestResp{
|
||||
{
|
||||
request: loopInSwap,
|
||||
response: &loop.LoopInSwapInfo{
|
||||
SwapHash: lntypes.Hash{2},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
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,
|
||||
|
|
@ -496,3 +839,24 @@ func existingSwapFromRequest(request *loop.OutRequest, initTime time.Time,
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func existingInFromRequest(in *loop.LoopInRequest, initTime time.Time,
|
||||
events []*loopdb.LoopEvent) *loopdb.LoopIn {
|
||||
|
||||
return &loopdb.LoopIn{
|
||||
Loop: loopdb.Loop{
|
||||
Events: events,
|
||||
},
|
||||
Contract: &loopdb.LoopInContract{
|
||||
SwapContract: loopdb.SwapContract{
|
||||
MaxSwapFee: in.MaxSwapFee,
|
||||
MaxMinerFee: in.MaxMinerFee,
|
||||
InitiationTime: initTime,
|
||||
Label: in.Label,
|
||||
},
|
||||
HtlcConfTarget: in.HtlcConfTarget,
|
||||
LastHop: in.LastHop,
|
||||
ExternalHtlc: in.ExternalHtlc,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue