Skip to content

Commit 55845ff

Browse files
authored
Merge pull request #548 from GeorgeTsagk/autoloop-amount-backoff
Autoloop amount backoff
2 parents ec4f250 + c17e5a6 commit 55845ff

9 files changed

+693
-344
lines changed

liquidity/autoloop_test.go

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -199,16 +199,30 @@ func TestAutoLoopEnabled(t *testing.T) {
199199
},
200200
},
201201
}
202+
203+
singleLoopOut = &loopdb.LoopOut{
204+
Loop: loopdb.Loop{
205+
Events: []*loopdb.LoopEvent{
206+
{
207+
SwapStateData: loopdb.SwapStateData{
208+
State: loopdb.StateInitiated,
209+
},
210+
},
211+
},
212+
},
213+
}
202214
)
203215

204216
// Tick our autolooper with no existing swaps, we expect a loop out
205217
// swap to be dispatched for each channel.
206218
step := &autoloopStep{
207-
minAmt: 1,
208-
maxAmt: amt + 1,
209-
quotesOut: quotes,
210-
expectedOut: loopOuts,
219+
minAmt: 1,
220+
maxAmt: amt + 1,
221+
quotesOut: quotes,
222+
expectedOut: loopOuts,
223+
existingOutSingle: singleLoopOut,
211224
}
225+
212226
c.autoloop(step)
213227

214228
// Tick again with both of our swaps in progress. We haven't shifted our
@@ -220,9 +234,10 @@ func TestAutoLoopEnabled(t *testing.T) {
220234
}
221235

222236
step = &autoloopStep{
223-
minAmt: 1,
224-
maxAmt: amt + 1,
225-
existingOut: existing,
237+
minAmt: 1,
238+
maxAmt: amt + 1,
239+
existingOut: existing,
240+
existingOutSingle: singleLoopOut,
226241
}
227242
c.autoloop(step)
228243

@@ -278,11 +293,12 @@ func TestAutoLoopEnabled(t *testing.T) {
278293
// still has balances which reflect that we need to swap), but nothing
279294
// for channel 2, since it has had a failure.
280295
step = &autoloopStep{
281-
minAmt: 1,
282-
maxAmt: amt + 1,
283-
existingOut: existing,
284-
quotesOut: quotes,
285-
expectedOut: loopOuts,
296+
minAmt: 1,
297+
maxAmt: amt + 1,
298+
existingOut: existing,
299+
quotesOut: quotes,
300+
expectedOut: loopOuts,
301+
existingOutSingle: singleLoopOut,
286302
}
287303
c.autoloop(step)
288304

@@ -299,10 +315,11 @@ func TestAutoLoopEnabled(t *testing.T) {
299315
}
300316

301317
step = &autoloopStep{
302-
minAmt: 1,
303-
maxAmt: amt + 1,
304-
existingOut: existing,
305-
quotesOut: quotes,
318+
minAmt: 1,
319+
maxAmt: amt + 1,
320+
existingOut: existing,
321+
quotesOut: quotes,
322+
existingOutSingle: singleLoopOut,
306323
}
307324
c.autoloop(step)
308325

@@ -446,13 +463,27 @@ func TestAutoloopAddress(t *testing.T) {
446463
},
447464
},
448465
}
466+
467+
singleLoopOut = &loopdb.LoopOut{
468+
Loop: loopdb.Loop{
469+
Events: []*loopdb.LoopEvent{
470+
{
471+
SwapStateData: loopdb.SwapStateData{
472+
State: loopdb.StateHtlcPublished,
473+
},
474+
},
475+
},
476+
},
477+
}
449478
)
450479

451480
step := &autoloopStep{
452-
minAmt: 1,
453-
maxAmt: amt + 1,
454-
quotesOut: quotes,
455-
expectedOut: loopOuts,
481+
minAmt: 1,
482+
maxAmt: amt + 1,
483+
quotesOut: quotes,
484+
expectedOut: loopOuts,
485+
existingOutSingle: singleLoopOut,
486+
keepDestAddr: true,
456487
}
457488
c.autoloop(step)
458489

@@ -606,17 +637,30 @@ func TestCompositeRules(t *testing.T) {
606637
},
607638
},
608639
}
640+
641+
singleLoopOut = &loopdb.LoopOut{
642+
Loop: loopdb.Loop{
643+
Events: []*loopdb.LoopEvent{
644+
{
645+
SwapStateData: loopdb.SwapStateData{
646+
State: loopdb.StateHtlcPublished,
647+
},
648+
},
649+
},
650+
},
651+
}
609652
)
610653

611654
// Tick our autolooper with no existing swaps, we expect a loop out
612655
// swap to be dispatched for each of our rules. We set our server side
613656
// maximum to be greater than the swap amount for our peer swap (which
614657
// is the larger of the two swaps).
615658
step := &autoloopStep{
616-
minAmt: 1,
617-
maxAmt: peerAmount + 1,
618-
quotesOut: quotes,
619-
expectedOut: loopOuts,
659+
minAmt: 1,
660+
maxAmt: peerAmount + 1,
661+
quotesOut: quotes,
662+
expectedOut: loopOuts,
663+
existingOutSingle: singleLoopOut,
620664
}
621665
c.autoloop(step)
622666

@@ -928,6 +972,18 @@ func TestAutoloopBothTypes(t *testing.T) {
928972
Label: labels.AutoloopLabel(swap.TypeIn),
929973
Initiator: autoloopSwapInitiator,
930974
}
975+
976+
singleLoopOut = &loopdb.LoopOut{
977+
Loop: loopdb.Loop{
978+
Events: []*loopdb.LoopEvent{
979+
{
980+
SwapStateData: loopdb.SwapStateData{
981+
State: loopdb.StateHtlcPublished,
982+
},
983+
},
984+
},
985+
},
986+
}
931987
)
932988

933989
step := &autoloopStep{
@@ -961,6 +1017,7 @@ func TestAutoloopBothTypes(t *testing.T) {
9611017
},
9621018
},
9631019
},
1020+
existingOutSingle: singleLoopOut,
9641021
}
9651022
c.autoloop(step)
9661023
c.stop()

liquidity/autoloop_testcontext_test.go

Lines changed: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package liquidity
22

33
import (
44
"context"
5+
"reflect"
56
"testing"
7+
"time"
68

79
"github.com/btcsuite/btcd/btcutil"
810
"github.com/lightninglabs/lndclient"
@@ -11,8 +13,10 @@ import (
1113
"github.com/lightninglabs/loop/swap"
1214
"github.com/lightninglabs/loop/test"
1315
"github.com/lightningnetwork/lnd/clock"
16+
"github.com/lightningnetwork/lnd/lntypes"
1417
"github.com/lightningnetwork/lnd/ticker"
1518
"github.com/stretchr/testify/assert"
19+
"github.com/stretchr/testify/require"
1620
)
1721

1822
type autoloopTestCtx struct {
@@ -45,9 +49,17 @@ type autoloopTestCtx struct {
4549
// loopOuts is a channel that we get existing loop out swaps on.
4650
loopOuts chan []*loopdb.LoopOut
4751

52+
// loopOutSingle is the single loop out returned from fetching a single
53+
// swap from store.
54+
loopOutSingle *loopdb.LoopOut
55+
4856
// loopIns is a channel that we get existing loop in swaps on.
4957
loopIns chan []*loopdb.LoopIn
5058

59+
// loopInSingle is the single loop in returned from fetching a single
60+
// swap from store.
61+
loopInSingle *loopdb.LoopIn
62+
5163
// restrictions is a channel that we get swap restrictions on.
5264
restrictions chan *Restrictions
5365

@@ -131,6 +143,9 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters,
131143
ListLoopOut: func() ([]*loopdb.LoopOut, error) {
132144
return <-testCtx.loopOuts, nil
133145
},
146+
GetLoopOut: func(hash lntypes.Hash) (*loopdb.LoopOut, error) {
147+
return testCtx.loopOutSingle, nil
148+
},
134149
ListLoopIn: func() ([]*loopdb.LoopIn, error) {
135150
return <-testCtx.loopIns, nil
136151
},
@@ -188,6 +203,10 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters,
188203
testCtx.manager = NewManager(cfg)
189204
err := testCtx.manager.setParameters(context.Background(), parameters)
190205
assert.NoError(t, err)
206+
// Override the payments check interval for the tests in order to not
207+
// timeout.
208+
testCtx.manager.params.CustomPaymentCheckInterval =
209+
150 * time.Millisecond
191210
<-done
192211
return testCtx
193212
}
@@ -241,14 +260,17 @@ type loopInRequestResp struct {
241260
// autoloopStep contains all of the information to required to step
242261
// through an autoloop tick.
243262
type autoloopStep struct {
244-
minAmt btcutil.Amount
245-
maxAmt btcutil.Amount
246-
existingOut []*loopdb.LoopOut
247-
existingIn []*loopdb.LoopIn
248-
quotesOut []quoteRequestResp
249-
quotesIn []quoteInRequestResp
250-
expectedOut []loopOutRequestResp
251-
expectedIn []loopInRequestResp
263+
minAmt btcutil.Amount
264+
maxAmt btcutil.Amount
265+
existingOut []*loopdb.LoopOut
266+
existingOutSingle *loopdb.LoopOut
267+
existingIn []*loopdb.LoopIn
268+
existingInSingle *loopdb.LoopIn
269+
quotesOut []quoteRequestResp
270+
quotesIn []quoteInRequestResp
271+
expectedOut []loopOutRequestResp
272+
expectedIn []loopInRequestResp
273+
keepDestAddr bool
252274
}
253275

254276
// autoloop walks our test context through the process of triggering our
@@ -269,6 +291,9 @@ func (c *autoloopTestCtx) autoloop(step *autoloopStep) {
269291
c.loopOuts <- step.existingOut
270292
c.loopIns <- step.existingIn
271293

294+
c.loopOutSingle = step.existingOutSingle
295+
c.loopInSingle = step.existingInSingle
296+
272297
// Assert that we query the server for a quote for each of our
273298
// recommended swaps. Note that this differs from our set of expected
274299
// swaps because we may get quotes for suggested swaps but then just
@@ -299,25 +324,77 @@ func (c *autoloopTestCtx) autoloop(step *autoloopStep) {
299324
c.quotes <- expected.quote
300325
}
301326

302-
// Assert that we dispatch the expected set of swaps.
303-
for _, expected := range step.expectedOut {
327+
require.True(c.t, c.matchLoopOuts(step.expectedOut, step.keepDestAddr))
328+
require.True(c.t, c.matchLoopIns(step.expectedIn))
329+
}
330+
331+
// matchLoopOuts checks that the actual loop out requests we got match the
332+
// expected ones. The argument keepDestAddr is used to indicate whether we keep
333+
// the actual loops destination address for the comparison. This is useful
334+
// because we don't want to compare the destination address generated by the
335+
// wallet mock. We want to compare the destination address when testing the
336+
// autoloop DestAddr parameter for loop outs.
337+
func (c *autoloopTestCtx) matchLoopOuts(swaps []loopOutRequestResp,
338+
keepDestAddr bool) bool {
339+
340+
swapsCopy := make([]loopOutRequestResp, len(swaps))
341+
copy(swapsCopy, swaps)
342+
343+
length := len(swapsCopy)
344+
345+
for i := 0; i < length; i++ {
304346
actual := <-c.outRequest
305347

306-
// Set our destination address to nil so that we do not need to
307-
// provide the address that is obtained by the mock wallet kit.
308-
if expected.request.DestAddr == nil {
348+
if !keepDestAddr {
309349
actual.DestAddr = nil
310350
}
311351

312-
assert.Equal(c.t, expected.request, actual)
313-
c.loopOut <- expected.response
352+
inner:
353+
for index, swap := range swapsCopy {
354+
equal := reflect.DeepEqual(swap.request, actual)
355+
356+
if equal {
357+
c.loopOut <- swap.response
358+
359+
swapsCopy = append(
360+
swapsCopy[:index],
361+
swapsCopy[index+1:]...,
362+
)
363+
364+
break inner
365+
}
366+
}
314367
}
315368

316-
for _, expected := range step.expectedIn {
369+
return len(swapsCopy) == 0
370+
}
371+
372+
// matchLoopIns checks that the actual loop in requests we got match the
373+
// expected ones.
374+
func (c *autoloopTestCtx) matchLoopIns(
375+
swaps []loopInRequestResp) bool {
376+
377+
swapsCopy := make([]loopInRequestResp, len(swaps))
378+
copy(swapsCopy, swaps)
379+
380+
for i := 0; i < len(swapsCopy); i++ {
317381
actual := <-c.inRequest
318382

319-
assert.Equal(c.t, expected.request, actual)
383+
inner:
384+
for i, swap := range swapsCopy {
385+
equal := reflect.DeepEqual(swap.request, actual)
320386

321-
c.loopIn <- expected.response
387+
if equal {
388+
c.loopIn <- swap.response
389+
390+
swapsCopy = append(
391+
swapsCopy[:i], swapsCopy[i+1:]...,
392+
)
393+
394+
break inner
395+
}
396+
}
322397
}
398+
399+
return len(swapsCopy) == 0
323400
}

0 commit comments

Comments
 (0)