diff --git a/routing/mock_test.go b/routing/mock_test.go index cf828baf..0d0b7a5b 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -333,6 +333,10 @@ func (m *mockControlTower) RegisterAttempt(phash lntypes.Hash, return channeldb.ErrPaymentAlreadyFailed } + if settled || failed { + return channeldb.ErrPaymentTerminal + } + // Add attempt to payment. p.attempts = append(p.attempts, channeldb.HTLCAttempt{ HTLCAttemptInfo: *a, diff --git a/routing/payment_lifecycle_test.go b/routing/payment_lifecycle_test.go index e8aa8c51..01383d21 100644 --- a/routing/payment_lifecycle_test.go +++ b/routing/payment_lifecycle_test.go @@ -650,6 +650,56 @@ func TestRouterPaymentStateMachine(t *testing.T) { }, paymentErr: channeldb.ErrPaymentAlreadyFailed, }, + { + // A MP payment scenario when our path finding returns + // after we've just received a terminal failure, and + // we have another shard still in flight. + name: "MP shard in flight after terminal", + + steps: []string{ + routerInitPayment, + + // shard 0 + routeRelease, + routerRegisterAttempt, + sendToSwitchSuccess, + + // shard 1 + routeRelease, + routerRegisterAttempt, + sendToSwitchSuccess, + + // shard 2 + routeRelease, + routerRegisterAttempt, + sendToSwitchSuccess, + + // We find a path for another shard. + routeRelease, + + // shard 0 fails with a terminal error. + getPaymentResultTerminalFailure, + routerFailAttempt, + routerFailPayment, + + // We try to register our final shard after + // processing a terminal failure. + routerRegisterAttempt, + + // Our in-flight shards fail. + getPaymentResultTempFailure, + getPaymentResultTempFailure, + routerFailAttempt, + routerFailAttempt, + + // Payment fails. + paymentError, + }, + routes: []*route.Route{ + shard, shard, shard, shard, + }, + paymentErr: channeldb.ErrPaymentTerminal, + }, } for _, test := range tests {