routing/payment_lifecycle_test: add MPP test cases

This commit is contained in:
Johan T. Halseth 2020-04-01 00:13:26 +02:00
parent 7b318a4be7
commit 431372c0cf
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26

@ -17,6 +17,8 @@ import (
"github.com/lightningnetwork/lnd/routing/route"
)
const stepTimeout = 5 * time.Second
// createTestRoute builds a route a->b->c paying the given amt to c.
func createTestRoute(amt lnwire.MilliSatoshi,
aliasMap map[string]route.Vertex) (*route.Route, error) {
@ -88,6 +90,11 @@ func TestRouterPaymentStateMachine(t *testing.T) {
t.Fatalf("unable to create route: %v", err)
}
shard, err := createTestRoute(paymentAmt/4, testGraph.aliasMap)
if err != nil {
t.Fatalf("unable to create route: %v", err)
}
// A payment state machine test case consists of several ordered steps,
// that we use for driving the scenario.
type testCase struct {
@ -369,17 +376,193 @@ func TestRouterPaymentStateMachine(t *testing.T) {
},
routes: []*route.Route{rt},
},
// =====================================
// || MPP scenarios ||
// =====================================
{
// Tests a simple successful MP payment of 4 shards.
steps: []string{
routerInitPayment,
// shard 0
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 1
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 2
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 3
routerRegisterAttempt,
sendToSwitchSuccess,
// All shards succeed.
getPaymentResultSuccess,
getPaymentResultSuccess,
getPaymentResultSuccess,
getPaymentResultSuccess,
// Router should settle them all.
routerSettleAttempt,
routerSettleAttempt,
routerSettleAttempt,
routerSettleAttempt,
// And the final result is obviously
// successful.
paymentSuccess,
},
routes: []*route.Route{shard, shard, shard, shard},
},
{
// An MP payment scenario where we need several extra
// attempts before the payment finally settle.
steps: []string{
routerInitPayment,
// shard 0
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 1
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 2
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 3
routerRegisterAttempt,
sendToSwitchSuccess,
// First two shards fail, two new ones are sent.
getPaymentResultTempFailure,
getPaymentResultTempFailure,
routerFailAttempt,
routerFailAttempt,
routerRegisterAttempt,
sendToSwitchSuccess,
routerRegisterAttempt,
sendToSwitchSuccess,
// The four shards settle.
getPaymentResultSuccess,
getPaymentResultSuccess,
getPaymentResultSuccess,
getPaymentResultSuccess,
routerSettleAttempt,
routerSettleAttempt,
routerSettleAttempt,
routerSettleAttempt,
// Overall payment succeeds.
paymentSuccess,
},
routes: []*route.Route{
shard, shard, shard, shard, shard, shard,
},
},
{
// An MP payment scenario where 3 of the shards fail.
// However the last shard settle, which means we get
// the preimage and should consider the overall payment
// a success.
steps: []string{
routerInitPayment,
// shard 0
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 1
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 2
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 3
routerRegisterAttempt,
sendToSwitchSuccess,
// 3 shards fail, and should be failed by the
// router.
getPaymentResultTempFailure,
getPaymentResultTempFailure,
getPaymentResultTempFailure,
routerFailAttempt,
routerFailAttempt,
routerFailAttempt,
// The fourth shard succeed against all odds,
// making the overall payment succeed.
getPaymentResultSuccess,
routerSettleAttempt,
paymentSuccess,
},
routes: []*route.Route{shard, shard, shard, shard},
},
{
// An MP payment scenario a shard fail with a terminal
// error, causing the router to stop attempting.
steps: []string{
routerInitPayment,
// shard 0
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 1
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 2
routerRegisterAttempt,
sendToSwitchSuccess,
// shard 3
routerRegisterAttempt,
sendToSwitchSuccess,
// The first shard fail with a terminal error.
getPaymentResultTerminalFailure,
routerFailAttempt,
routerFailPayment,
// Remaining 3 shards fail.
getPaymentResultTempFailure,
getPaymentResultTempFailure,
getPaymentResultTempFailure,
routerFailAttempt,
routerFailAttempt,
routerFailAttempt,
// Payment fails.
paymentError,
},
routes: []*route.Route{
shard, shard, shard, shard, shard, shard,
},
},
}
// Create a mock control tower with channels set up, that we use to
// synchronize and listen for events.
control := makeMockControlTower()
control.init = make(chan initArgs)
control.registerAttempt = make(chan registerAttemptArgs)
control.settleAttempt = make(chan settleAttemptArgs)
control.failAttempt = make(chan failAttemptArgs)
control.failPayment = make(chan failPaymentArgs)
control.fetchInFlight = make(chan struct{})
control.init = make(chan initArgs, 20)
control.registerAttempt = make(chan registerAttemptArgs, 20)
control.settleAttempt = make(chan settleAttemptArgs, 20)
control.failAttempt = make(chan failAttemptArgs, 20)
control.failPayment = make(chan failPaymentArgs, 20)
control.fetchInFlight = make(chan struct{}, 20)
quit := make(chan struct{})
defer close(quit)
@ -502,7 +685,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
var args initArgs
select {
case args = <-control.init:
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("no init payment with control")
}
@ -516,7 +699,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
var args registerAttemptArgs
select {
case args = <-control.registerAttempt:
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("attempt not registered " +
"with control")
}
@ -530,7 +713,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
case routerSettleAttempt:
select {
case <-control.settleAttempt:
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("attempt settle not " +
"registered with control")
}
@ -541,7 +724,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
case routerFailAttempt:
select {
case <-control.failAttempt:
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("attempt fail not " +
"registered with control")
}
@ -552,7 +735,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
case routerFailPayment:
select {
case <-control.failPayment:
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("payment fail not " +
"registered with control")
}
@ -562,7 +745,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
case sendToSwitchSuccess:
select {
case sendResult <- nil:
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("unable to send result")
}
@ -574,7 +757,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
&lnwire.FailTemporaryChannelFailure{},
1,
):
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("unable to send result")
}
@ -586,7 +769,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
case getPaymentResult <- &htlcswitch.PaymentResult{
Preimage: preImage,
}:
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("unable to send result")
}
@ -603,7 +786,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
case getPaymentResult <- &htlcswitch.PaymentResult{
Error: failure,
}:
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("unable to get result")
}
@ -621,7 +804,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
case getPaymentResult <- &htlcswitch.PaymentResult{
Error: failure,
}:
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("unable to get result")
}
@ -640,7 +823,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
select {
case getPaymentResultErr <- fmt.Errorf(
"shutting down"):
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("unable to send payment " +
"result error")
}
@ -663,7 +846,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
t.Fatalf("expected error")
}
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("got no payment result")
}
@ -677,7 +860,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
"error %v", err)
}
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("got no payment result")
}
@ -690,7 +873,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
t.Fatalf("expected error")
}
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("got no payment result")
}
@ -703,7 +886,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
t.Fatalf("did not expect error %v", err)
}
case <-time.After(1 * time.Second):
case <-time.After(stepTimeout):
t.Fatalf("got no payment result")
}