2
0
mirror of https://github.com/lightninglabs/loop synced 2024-11-16 00:12:52 +00:00
loop/loopin_test.go

461 lines
10 KiB
Go
Raw Normal View History

2019-03-12 15:10:37 +00:00
package loop
import (
"context"
"testing"
"github.com/lightninglabs/lndclient"
2019-03-12 15:10:37 +00:00
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
2020-06-25 10:35:22 +00:00
"github.com/stretchr/testify/require"
2019-03-12 15:10:37 +00:00
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
var (
testLoopInRequest = LoopInRequest{
Amount: btcutil.Amount(50000),
MaxSwapFee: btcutil.Amount(1000),
HtlcConfTarget: 2,
}
)
// TestLoopInSuccess tests the success scenario where the swap completes the
// happy flow.
func TestLoopInSuccess(t *testing.T) {
defer test.Guard(t)()
ctx := newLoopInTestContext(t)
height := int32(600)
2020-06-08 10:53:07 +00:00
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server)
2019-03-12 15:10:37 +00:00
2020-06-30 12:06:36 +00:00
initResult, err := newLoopInSwap(
2019-03-12 15:10:37 +00:00
context.Background(), cfg,
height, &testLoopInRequest,
)
if err != nil {
t.Fatal(err)
}
2020-06-30 12:06:36 +00:00
swap := initResult.swap
2019-03-12 15:10:37 +00:00
ctx.store.assertLoopInStored()
errChan := make(chan error)
go func() {
err := swap.execute(context.Background(), ctx.cfg, height)
if err != nil {
2019-10-28 16:06:07 +00:00
log.Error(err)
2019-03-12 15:10:37 +00:00
}
errChan <- err
}()
ctx.assertState(loopdb.StateInitiated)
ctx.assertState(loopdb.StateHtlcPublished)
ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
// Expect htlc to be published.
htlcTx := <-ctx.lnd.SendOutputsChannel
2020-06-25 10:35:22 +00:00
// Expect the same state to be written again with the htlc tx hash.
state := ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
require.NotNil(t, state.HtlcTxHash)
2019-03-12 15:10:37 +00:00
// Expect register for htlc conf.
<-ctx.lnd.RegisterConfChannel
<-ctx.lnd.RegisterConfChannel
2019-03-12 15:10:37 +00:00
// Confirm htlc.
ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{
Tx: &htlcTx,
}
// Client starts listening for spend of htlc.
<-ctx.lnd.RegisterSpendChannel
// Client starts listening for swap invoice updates.
subscription := <-ctx.lnd.SingleInvoiceSubcribeChannel
if subscription.Hash != ctx.server.swapHash {
t.Fatal("client subscribing to wrong invoice")
}
// Server has already paid invoice before spending the htlc. Signal
// settled.
subscription.Update <- lndclient.InvoiceUpdate{
State: channeldb.ContractSettled,
AmtPaid: 49000,
}
// Swap is expected to move to the state InvoiceSettled
ctx.assertState(loopdb.StateInvoiceSettled)
ctx.store.assertLoopInState(loopdb.StateInvoiceSettled)
// Server spends htlc.
successTx := wire.MsgTx{}
successTx.AddTxIn(&wire.TxIn{
Witness: [][]byte{{}, {}, {}},
})
ctx.lnd.SpendChannel <- &chainntnfs.SpendDetail{
SpendingTx: &successTx,
SpenderInputIndex: 0,
}
2019-03-12 15:10:37 +00:00
ctx.assertState(loopdb.StateSuccess)
ctx.store.assertLoopInState(loopdb.StateSuccess)
err = <-errChan
if err != nil {
t.Fatal(err)
}
}
// TestLoopInTimeout tests scenarios where the server doesn't sweep the htlc
2019-03-12 15:10:37 +00:00
// and the client is forced to reclaim the funds using the timeout tx.
func TestLoopInTimeout(t *testing.T) {
testAmt := int64(testLoopInRequest.Amount)
t.Run("internal htlc", func(t *testing.T) {
testLoopInTimeout(t, swap.HtlcP2WSH, 0)
})
outputTypes := []swap.HtlcOutputType{swap.HtlcP2WSH, swap.HtlcNP2WSH}
for _, outputType := range outputTypes {
outputType := outputType
t.Run(outputType.String(), func(t *testing.T) {
t.Run("external htlc", func(t *testing.T) {
testLoopInTimeout(t, outputType, testAmt)
})
t.Run("external amount too high", func(t *testing.T) {
testLoopInTimeout(t, outputType, testAmt+1)
})
t.Run("external amount too low", func(t *testing.T) {
testLoopInTimeout(t, outputType, testAmt-1)
})
})
}
}
func testLoopInTimeout(t *testing.T,
outputType swap.HtlcOutputType, externalValue int64) {
2019-03-12 15:10:37 +00:00
defer test.Guard(t)()
ctx := newLoopInTestContext(t)
height := int32(600)
2020-06-08 10:53:07 +00:00
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server)
2019-03-12 15:10:37 +00:00
req := testLoopInRequest
if externalValue != 0 {
req.ExternalHtlc = true
}
2020-06-30 12:06:36 +00:00
initResult, err := newLoopInSwap(
2019-03-12 15:10:37 +00:00
context.Background(), cfg,
height, &req,
2019-03-12 15:10:37 +00:00
)
if err != nil {
t.Fatal(err)
}
2020-06-30 12:06:36 +00:00
s := initResult.swap
2019-03-12 15:10:37 +00:00
ctx.store.assertLoopInStored()
errChan := make(chan error)
go func() {
err := s.execute(context.Background(), ctx.cfg, height)
2019-03-12 15:10:37 +00:00
if err != nil {
2019-10-28 16:06:07 +00:00
log.Error(err)
2019-03-12 15:10:37 +00:00
}
errChan <- err
}()
ctx.assertState(loopdb.StateInitiated)
ctx.assertState(loopdb.StateHtlcPublished)
ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
var htlcTx wire.MsgTx
if externalValue == 0 {
// Expect htlc to be published.
htlcTx = <-ctx.lnd.SendOutputsChannel
2020-06-25 10:35:22 +00:00
// Expect the same state to be written again with the htlc tx hash.
state := ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
require.NotNil(t, state.HtlcTxHash)
} else {
// Create an external htlc publish tx.
var pkScript []byte
if outputType == swap.HtlcNP2WSH {
pkScript = s.htlcNP2WSH.PkScript
} else {
pkScript = s.htlcP2WSH.PkScript
}
htlcTx = wire.MsgTx{
TxOut: []*wire.TxOut{
{
PkScript: pkScript,
Value: externalValue,
},
},
}
}
2019-03-12 15:10:37 +00:00
// Expect register for htlc conf.
<-ctx.lnd.RegisterConfChannel
<-ctx.lnd.RegisterConfChannel
2019-03-12 15:10:37 +00:00
// Confirm htlc.
ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{
Tx: &htlcTx,
}
// Assert that the swap is failed in case of an invalid amount.
invalidAmt := externalValue != 0 && externalValue != int64(req.Amount)
if invalidAmt {
ctx.assertState(loopdb.StateFailIncorrectHtlcAmt)
ctx.store.assertLoopInState(loopdb.StateFailIncorrectHtlcAmt)
err = <-errChan
if err != nil {
t.Fatal(err)
}
return
}
2019-03-12 15:10:37 +00:00
// Client starts listening for spend of htlc.
<-ctx.lnd.RegisterSpendChannel
// Client starts listening for swap invoice updates.
subscription := <-ctx.lnd.SingleInvoiceSubcribeChannel
if subscription.Hash != ctx.server.swapHash {
t.Fatal("client subscribing to wrong invoice")
}
2019-03-12 15:10:37 +00:00
// Let htlc expire.
ctx.blockEpochChan <- s.LoopInContract.CltvExpiry
2019-03-12 15:10:37 +00:00
// Expect a signing request for the htlc tx output value.
signReq := <-ctx.lnd.SignOutputRawChannel
if signReq.SignDescriptors[0].Output.Value != htlcTx.TxOut[0].Value {
t.Fatal("invalid signing amount")
}
2019-03-12 15:10:37 +00:00
// Expect timeout tx to be published.
timeoutTx := <-ctx.lnd.TxPublishChannel
// Confirm timeout tx.
ctx.lnd.SpendChannel <- &chainntnfs.SpendDetail{
SpendingTx: timeoutTx,
SpenderInputIndex: 0,
}
// Now that timeout tx has confirmed, the client should be able to
// safely cancel the swap invoice.
<-ctx.lnd.FailInvoiceChannel
// Signal the the invoice was canceled.
subscription.Update <- lndclient.InvoiceUpdate{
State: channeldb.ContractCanceled,
}
ctx.assertState(loopdb.StateFailTimeout)
ctx.store.assertLoopInState(loopdb.StateFailTimeout)
err = <-errChan
if err != nil {
t.Fatal(err)
}
}
// TestLoopInResume tests resuming swaps in various states.
func TestLoopInResume(t *testing.T) {
t.Run("initiated", func(t *testing.T) {
testLoopInResume(t, loopdb.StateInitiated, false)
})
t.Run("initiated expired", func(t *testing.T) {
testLoopInResume(t, loopdb.StateInitiated, true)
})
t.Run("htlc published", func(t *testing.T) {
testLoopInResume(t, loopdb.StateHtlcPublished, false)
})
}
func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool) {
defer test.Guard(t)()
ctx := newLoopInTestContext(t)
2020-06-08 10:53:07 +00:00
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server)
2019-03-12 15:10:37 +00:00
senderKey := [33]byte{4}
receiverKey := [33]byte{5}
contract := &loopdb.LoopInContract{
HtlcConfTarget: 2,
SwapContract: loopdb.SwapContract{
Preimage: testPreimage,
AmountRequested: 100000,
CltvExpiry: 744,
ReceiverKey: receiverKey,
SenderKey: senderKey,
MaxSwapFee: 60000,
MaxMinerFee: 50000,
},
}
pendSwap := &loopdb.LoopIn{
Contract: contract,
Loop: loopdb.Loop{
Events: []*loopdb.LoopEvent{
{
SwapStateData: loopdb.SwapStateData{
State: state,
},
2019-03-12 15:10:37 +00:00
},
},
Hash: testPreimage.Hash(),
},
}
htlc, err := swap.NewHtlc(
contract.CltvExpiry, contract.SenderKey, contract.ReceiverKey,
2019-04-04 10:20:45 +00:00
testPreimage.Hash(), swap.HtlcNP2WSH, cfg.lnd.ChainParams,
2019-03-12 15:10:37 +00:00
)
if err != nil {
t.Fatal(err)
}
err = ctx.store.CreateLoopIn(testPreimage.Hash(), contract)
if err != nil {
t.Fatal(err)
}
swap, err := resumeLoopInSwap(
context.Background(), cfg,
pendSwap,
)
if err != nil {
t.Fatal(err)
}
var height int32
if expired {
height = 740
} else {
height = 600
}
errChan := make(chan error)
go func() {
err := swap.execute(context.Background(), ctx.cfg, height)
if err != nil {
2019-10-28 16:06:07 +00:00
log.Error(err)
2019-03-12 15:10:37 +00:00
}
errChan <- err
}()
defer func() {
err = <-errChan
if err != nil {
t.Fatal(err)
}
select {
case <-ctx.lnd.SendPaymentChannel:
t.Fatal("unexpected payment sent")
default:
}
select {
case <-ctx.lnd.SendOutputsChannel:
t.Fatal("unexpected tx published")
default:
}
}()
var htlcTx wire.MsgTx
if state == loopdb.StateInitiated {
ctx.assertState(loopdb.StateInitiated)
if expired {
ctx.assertState(loopdb.StateFailTimeout)
return
}
ctx.assertState(loopdb.StateHtlcPublished)
ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
// Expect htlc to be published.
htlcTx = <-ctx.lnd.SendOutputsChannel
2020-06-25 10:35:22 +00:00
// Expect the same state to be written again with the htlc tx
// hash.
state := ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
require.NotNil(t, state.HtlcTxHash)
2019-03-12 15:10:37 +00:00
} else {
ctx.assertState(loopdb.StateHtlcPublished)
htlcTx.AddTxOut(&wire.TxOut{
2019-04-04 10:20:45 +00:00
PkScript: htlc.PkScript,
Value: int64(contract.AmountRequested),
2019-03-12 15:10:37 +00:00
})
}
// Expect register for htlc conf.
<-ctx.lnd.RegisterConfChannel
<-ctx.lnd.RegisterConfChannel
2019-03-12 15:10:37 +00:00
// Confirm htlc.
ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{
Tx: &htlcTx,
}
// Client starts listening for spend of htlc.
<-ctx.lnd.RegisterSpendChannel
// Client starts listening for swap invoice updates.
subscription := <-ctx.lnd.SingleInvoiceSubcribeChannel
if subscription.Hash != testPreimage.Hash() {
t.Fatal("client subscribing to wrong invoice")
}
// Server has already paid invoice before spending the htlc. Signal
// settled.
subscription.Update <- lndclient.InvoiceUpdate{
State: channeldb.ContractSettled,
AmtPaid: 49000,
}
// Swap is expected to move to the state InvoiceSettled
ctx.assertState(loopdb.StateInvoiceSettled)
ctx.store.assertLoopInState(loopdb.StateInvoiceSettled)
// Server spends htlc.
successTx := wire.MsgTx{}
successTx.AddTxIn(&wire.TxIn{
Witness: [][]byte{{}, {}, {}},
})
successTxHash := successTx.TxHash()
ctx.lnd.SpendChannel <- &chainntnfs.SpendDetail{
SpendingTx: &successTx,
SpenderTxHash: &successTxHash,
SpenderInputIndex: 0,
}
2019-03-12 15:10:37 +00:00
ctx.assertState(loopdb.StateSuccess)
}