loopout: cancel swap with server when off-chain fails

This commit is contained in:
carla 2021-05-24 08:46:03 +02:00
parent 6b732bacee
commit f166ce899d
No known key found for this signature in database
GPG key ID: 4CA7FE54A6213C91
4 changed files with 264 additions and 2 deletions

View file

@ -3,6 +3,7 @@ package loop
import (
"context"
"errors"
"math"
"reflect"
"testing"
"time"
@ -16,6 +17,7 @@ import (
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/zpay32"
"github.com/stretchr/testify/require"
)
@ -701,3 +703,140 @@ func TestExpiryBeforeReveal(t *testing.T) {
require.Nil(t, <-errChan)
}
// TestFailedOffChainCancelation tests sending of a cancelation message to
// the server when a swap fails due to off-chain routing.
func TestFailedOffChainCancelation(t *testing.T) {
defer test.Guard(t)()
lnd := test.NewMockLnd()
ctx := test.NewContext(t, lnd)
server := newServerMock(lnd)
testReq := *testRequest
testReq.Expiry = lnd.Height + 20
cfg := newSwapConfig(
&lnd.LndServices, newStoreMock(t), server,
)
initResult, err := newLoopOutSwap(
context.Background(), cfg, lnd.Height, &testReq,
)
require.NoError(t, err)
swap := initResult.swap
// Set up the required dependencies to execute the swap.
sweeper := &sweep.Sweeper{Lnd: &lnd.LndServices}
blockEpochChan := make(chan interface{})
statusChan := make(chan SwapInfo)
expiryChan := make(chan time.Time)
timerFactory := func(_ time.Duration) <-chan time.Time {
return expiryChan
}
errChan := make(chan error)
go func() {
cfg := &executeConfig{
statusChan: statusChan,
sweeper: sweeper,
blockEpochChan: blockEpochChan,
timerFactory: timerFactory,
cancelSwap: server.CancelLoopOutSwap,
}
err := swap.execute(context.Background(), cfg, ctx.Lnd.Height)
errChan <- err
}()
// The swap should be found in its initial state.
cfg.store.(*storeMock).assertLoopOutStored()
state := <-statusChan
require.Equal(t, loopdb.StateInitiated, state.State)
// Assert that we register for htlc confirmation notifications.
ctx.AssertRegisterConf(false, defaultConfirmations)
// We expect prepayment and invoice to be dispatched, order is unknown.
pmt1 := <-ctx.Lnd.RouterSendPaymentChannel
pmt2 := <-ctx.Lnd.RouterSendPaymentChannel
failUpdate := lndclient.PaymentStatus{
State: lnrpc.Payment_FAILED,
FailureReason: lnrpc.PaymentFailureReason_FAILURE_REASON_ERROR,
Htlcs: []*lndclient.HtlcAttempt{
{
// Include a non-failed htlc to test that we
// only report failed htlcs.
Status: lnrpc.HTLCAttempt_IN_FLIGHT,
},
// Add one htlc that failed within the server's
// infrastructure.
{
Status: lnrpc.HTLCAttempt_FAILED,
Route: &lnrpc.Route{
Hops: []*lnrpc.Hop{
{}, {}, {},
},
},
Failure: &lndclient.HtlcFailure{
FailureSourceIndex: 1,
},
},
// Add one htlc that failed in the network at wide.
{
Status: lnrpc.HTLCAttempt_FAILED,
Route: &lnrpc.Route{
Hops: []*lnrpc.Hop{
{}, {}, {}, {}, {},
},
},
Failure: &lndclient.HtlcFailure{
FailureSourceIndex: 1,
},
},
},
}
successUpdate := lndclient.PaymentStatus{
State: lnrpc.Payment_SUCCEEDED,
}
// We want to fail our swap payment and succeed the prepush, so we send
// a failure update to the payment that has the larger amount.
if pmt1.Amount > pmt2.Amount {
pmt1.TrackPaymentMessage.Updates <- failUpdate
pmt2.TrackPaymentMessage.Updates <- successUpdate
} else {
pmt1.TrackPaymentMessage.Updates <- successUpdate
pmt2.TrackPaymentMessage.Updates <- failUpdate
}
invoice, err := zpay32.Decode(
swap.LoopOutContract.SwapInvoice, lnd.ChainParams,
)
require.NoError(t, err)
require.NotNil(t, invoice.PaymentAddr)
swapCancelation := &outCancelDetails{
hash: swap.hash,
paymentAddr: *invoice.PaymentAddr,
metadata: routeCancelMetadata{
paymentType: paymentTypeInvoice,
failureReason: failUpdate.FailureReason,
attempts: []uint32{
2,
math.MaxUint32,
},
},
}
server.assertSwapCanceled(t, swapCancelation)
// Finally, the swap should be recorded with failed off chain timeout.
cfg.store.(*storeMock).assertLoopOutState(
loopdb.StateFailOffchainPayments,
)
state = <-statusChan
require.Equal(t, state.State, loopdb.StateFailOffchainPayments)
require.NoError(t, <-errChan)
}