From 1041bedb664a74a6aa3e88004fb86bbeb80bdef5 Mon Sep 17 00:00:00 2001 From: bruwbird Date: Fri, 24 May 2024 08:46:47 +0900 Subject: [PATCH] test: add integration test for LWK and liquid The integration test for LWK and liquid is added to ensure that the swap functionality works as expected with the LWK and electrs support. --- test/lwk_cln_test.go | 486 +++++++++++++++++++++++++++++++++++++++++++ test/setup.go | 241 +++++++++++++++++++++ 2 files changed, 727 insertions(+) diff --git a/test/lwk_cln_test.go b/test/lwk_cln_test.go index 69f67abb..1e5dddde 100644 --- a/test/lwk_cln_test.go +++ b/test/lwk_cln_test.go @@ -994,3 +994,489 @@ func Test_ClnLnd_LWK_SwapOut(t *testing.T) { csvClaimTest(t, params) }) } + +func Test_ClnCln_LWKElements_SwapIn(t *testing.T) { + IsIntegrationTest(t) + t.Parallel() + t.Run("claim_normal", func(t *testing.T) { + t.Parallel() + require := require.New(t) + + bitcoind, liquidd, lightningds, scid, electrs, lwk := clnclnLWKLiquidSetup(t, uint64(math.Pow10(9))) + defer func() { + if t.Failed() { + filter := os.Getenv("PEERSWAP_TEST_FILTER") + pprintFail( + tailableProcess{ + p: bitcoind.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: liquidd.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[0].DaemonProcess, + filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[1].DaemonProcess, + // filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: electrs.Process, + lines: defaultLines, + }, + tailableProcess{ + p: lwk.Process, + lines: defaultLines, + }, + ) + } + }() + + var channelBalances []uint64 + var walletBalances []uint64 + for _, lightningd := range lightningds { + b, err := lightningd.GetBtcBalanceSat() + require.NoError(err) + walletBalances = append(walletBalances, b) + + b, err = lightningd.GetChannelBalanceSat(scid) + require.NoError(err) + channelBalances = append(channelBalances, b) + } + + params := &testParams{ + swapAmt: channelBalances[0] / 2, + scid: scid, + origTakerWallet: walletBalances[0], + origMakerWallet: walletBalances[1], + origTakerBalance: channelBalances[0], + origMakerBalance: channelBalances[1], + takerNode: lightningds[0], + makerNode: lightningds[1], + takerPeerswap: lightningds[0].DaemonProcess, + makerPeerswap: lightningds[1].DaemonProcess, + chainRpc: liquidd.RpcProxy, + chaind: liquidd, + confirms: LiquidConfirms, + csv: LiquidCsv, + swapType: swap.SWAPTYPE_IN, + } + asset := "lbtc" + + // Do swap. + go func() { + var response map[string]interface{} + err := lightningds[1].Rpc.Request(&clightning.SwapIn{SatAmt: params.swapAmt, ShortChannelId: params.scid, Asset: asset}, &response) + require.NoError(err) + + }() + preimageClaimTest(t, params) + }) + t.Run("claim_coop", func(t *testing.T) { + t.Parallel() + require := require.New(t) + + bitcoind, liquidd, lightningds, scid, electrs, lwk := clnclnLWKLiquidSetup(t, uint64(math.Pow10(9))) + defer func() { + if t.Failed() { + filter := os.Getenv("PEERSWAP_TEST_FILTER") + pprintFail( + tailableProcess{ + p: bitcoind.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: liquidd.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[0].DaemonProcess, + filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[1].DaemonProcess, + filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: electrs.Process, + lines: defaultLines, + }, + tailableProcess{ + p: lwk.Process, + lines: defaultLines, + }, + ) + } + }() + + var channelBalances []uint64 + var walletBalances []uint64 + for _, lightningd := range lightningds { + b, err := lightningd.GetBtcBalanceSat() + require.NoError(err) + walletBalances = append(walletBalances, b) + + b, err = lightningd.GetChannelBalanceSat(scid) + require.NoError(err) + channelBalances = append(channelBalances, b) + } + + params := &testParams{ + swapAmt: channelBalances[0] / 2, + scid: scid, + origTakerWallet: walletBalances[0], + origMakerWallet: walletBalances[1], + origTakerBalance: channelBalances[0], + origMakerBalance: channelBalances[1], + takerNode: lightningds[0], + makerNode: lightningds[1], + takerPeerswap: lightningds[0].DaemonProcess, + makerPeerswap: lightningds[1].DaemonProcess, + chainRpc: liquidd.RpcProxy, + chaind: liquidd, + confirms: LiquidConfirms, + csv: LiquidCsv, + swapType: swap.SWAPTYPE_IN, + } + asset := "lbtc" + + // Do swap. + go func() { + var response map[string]interface{} + err := lightningds[1].Rpc.Request(&clightning.SwapIn{SatAmt: params.swapAmt, ShortChannelId: params.scid, Asset: asset}, &response) + require.NoError(err) + + }() + coopClaimTest(t, params) + }) + + t.Run("claim_csv", func(t *testing.T) { + t.Parallel() + require := require.New(t) + + bitcoind, liquidd, lightningds, scid, electrs, lwk := clnclnLWKLiquidSetup(t, uint64(math.Pow10(9))) + defer func() { + if t.Failed() { + filter := os.Getenv("PEERSWAP_TEST_FILTER") + pprintFail( + tailableProcess{ + p: bitcoind.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: liquidd.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[0].DaemonProcess, + filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[1].DaemonProcess, + filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: electrs.Process, + lines: defaultLines, + }, + tailableProcess{ + p: lwk.Process, + lines: defaultLines, + }, + ) + } + }() + + var channelBalances []uint64 + var walletBalances []uint64 + for _, lightningd := range lightningds { + b, err := lightningd.GetBtcBalanceSat() + require.NoError(err) + walletBalances = append(walletBalances, b) + + b, err = lightningd.GetChannelBalanceSat(scid) + require.NoError(err) + channelBalances = append(channelBalances, b) + } + + params := &testParams{ + swapAmt: channelBalances[0] / 2, + scid: scid, + origTakerWallet: walletBalances[0], + origMakerWallet: walletBalances[1], + origTakerBalance: channelBalances[0], + origMakerBalance: channelBalances[1], + takerNode: lightningds[0], + makerNode: lightningds[1], + takerPeerswap: lightningds[0].DaemonProcess, + makerPeerswap: lightningds[1].DaemonProcess, + chainRpc: liquidd.RpcProxy, + chaind: liquidd, + confirms: LiquidConfirms, + csv: LiquidCsv, + swapType: swap.SWAPTYPE_IN, + } + asset := "lbtc" + + // Do swap. + go func() { + var response map[string]interface{} + err := lightningds[1].Rpc.Request(&clightning.SwapIn{SatAmt: params.swapAmt, ShortChannelId: params.scid, Asset: asset}, &response) + require.NoError(err) + + }() + csvClaimTest(t, params) + }) +} + +func Test_ClnCln_LWKLiquid_SwapOut(t *testing.T) { + IsIntegrationTest(t) + t.Parallel() + t.Run("claim_normal", func(t *testing.T) { + t.Parallel() + require := require.New(t) + + bitcoind, liquidd, lightningds, scid, electrs, lwk := clnclnLWKLiquidSetup(t, uint64(math.Pow10(9))) + defer func() { + if t.Failed() { + filter := os.Getenv("PEERSWAP_TEST_FILTER") + pprintFail( + tailableProcess{ + p: bitcoind.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: liquidd.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[0].DaemonProcess, + filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[1].DaemonProcess, + filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: electrs.Process, + lines: defaultLines, + }, + tailableProcess{ + p: lwk.Process, + lines: defaultLines, + }, + ) + } + }() + + var channelBalances []uint64 + var walletBalances []uint64 + for _, lightningd := range lightningds { + b, err := lightningd.GetBtcBalanceSat() + require.NoError(err) + walletBalances = append(walletBalances, b) + + b, err = lightningd.GetChannelBalanceSat(scid) + require.NoError(err) + channelBalances = append(channelBalances, b) + } + + params := &testParams{ + swapAmt: channelBalances[0] / 2, + scid: scid, + origTakerWallet: walletBalances[0], + origMakerWallet: walletBalances[1], + origTakerBalance: channelBalances[0], + origMakerBalance: channelBalances[1], + takerNode: lightningds[0], + makerNode: lightningds[1], + takerPeerswap: lightningds[0].DaemonProcess, + makerPeerswap: lightningds[1].DaemonProcess, + chainRpc: liquidd.RpcProxy, + chaind: liquidd, + confirms: LiquidConfirms, + csv: LiquidCsv, + swapType: swap.SWAPTYPE_OUT, + } + asset := "lbtc" + + // Do swap. + go func() { + var response map[string]interface{} + err := lightningds[0].Rpc.Request(&clightning.SwapOut{SatAmt: params.swapAmt, ShortChannelId: params.scid, Asset: asset}, &response) + require.NoError(err) + + }() + preimageClaimTest(t, params) + }) + t.Run("claim_coop", func(t *testing.T) { + t.Parallel() + require := require.New(t) + + bitcoind, liquidd, lightningds, scid, electrs, lwk := clnclnLWKSetup(t, uint64(math.Pow10(9))) + defer func() { + if t.Failed() { + filter := os.Getenv("PEERSWAP_TEST_FILTER") + pprintFail( + tailableProcess{ + p: bitcoind.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: liquidd.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[0].DaemonProcess, + filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[1].DaemonProcess, + filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: electrs.Process, + lines: defaultLines, + }, + tailableProcess{ + p: lwk.Process, + lines: defaultLines, + }, + ) + } + }() + + var channelBalances []uint64 + var walletBalances []uint64 + for _, lightningd := range lightningds { + b, err := lightningd.GetBtcBalanceSat() + require.NoError(err) + walletBalances = append(walletBalances, b) + + b, err = lightningd.GetChannelBalanceSat(scid) + require.NoError(err) + channelBalances = append(channelBalances, b) + } + + params := &testParams{ + swapAmt: channelBalances[0] / 2, + scid: scid, + origTakerWallet: walletBalances[0], + origMakerWallet: walletBalances[1], + origTakerBalance: channelBalances[0], + origMakerBalance: channelBalances[1], + takerNode: lightningds[0], + makerNode: lightningds[1], + takerPeerswap: lightningds[0].DaemonProcess, + makerPeerswap: lightningds[1].DaemonProcess, + chainRpc: liquidd.RpcProxy, + chaind: liquidd, + confirms: LiquidConfirms, + csv: LiquidCsv, + swapType: swap.SWAPTYPE_OUT, + } + asset := "lbtc" + + // Do swap. + go func() { + var response map[string]interface{} + err := lightningds[0].Rpc.Request(&clightning.SwapOut{SatAmt: params.swapAmt, ShortChannelId: params.scid, Asset: asset}, &response) + require.NoError(err) + + }() + coopClaimTest(t, params) + }) + + t.Run("claim_csv", func(t *testing.T) { + t.Parallel() + require := require.New(t) + + bitcoind, liquidd, lightningds, scid, electrs, lwk := clnclnLWKSetup(t, uint64(math.Pow10(9))) + defer func() { + if t.Failed() { + filter := os.Getenv("PEERSWAP_TEST_FILTER") + pprintFail( + tailableProcess{ + p: bitcoind.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: liquidd.DaemonProcess, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[0].DaemonProcess, + filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: lightningds[1].DaemonProcess, + filter: filter, + lines: defaultLines, + }, + tailableProcess{ + p: electrs.Process, + lines: defaultLines, + }, + tailableProcess{ + p: lwk.Process, + lines: defaultLines, + }, + ) + } + }() + + var channelBalances []uint64 + var walletBalances []uint64 + for _, lightningd := range lightningds { + b, err := lightningd.GetBtcBalanceSat() + require.NoError(err) + walletBalances = append(walletBalances, b) + + b, err = lightningd.GetChannelBalanceSat(scid) + require.NoError(err) + channelBalances = append(channelBalances, b) + } + + params := &testParams{ + swapAmt: channelBalances[0] / 2, + scid: scid, + origTakerWallet: walletBalances[0], + origMakerWallet: walletBalances[1], + origTakerBalance: channelBalances[0], + origMakerBalance: channelBalances[1], + takerNode: lightningds[0], + makerNode: lightningds[1], + takerPeerswap: lightningds[0].DaemonProcess, + makerPeerswap: lightningds[1].DaemonProcess, + chainRpc: liquidd.RpcProxy, + chaind: liquidd, + confirms: LiquidConfirms, + csv: LiquidCsv, + swapType: swap.SWAPTYPE_OUT, + } + asset := "lbtc" + + // Do swap. + go func() { + var response map[string]interface{} + err := lightningds[0].Rpc.Request(&clightning.SwapOut{SatAmt: params.swapAmt, ShortChannelId: params.scid, Asset: asset}, &response) + require.NoError(err) + + }() + csvClaimTest(t, params) + }) +} diff --git a/test/setup.go b/test/setup.go index 155ac042..903f23c5 100644 --- a/test/setup.go +++ b/test/setup.go @@ -1336,3 +1336,244 @@ func mixedLWKSetup(t *testing.T, fundAmt uint64, funder fundingNode) (*testframe syncPoll(&clnPollableNode{cln}, &peerswapPollableNode{peerswapd, lnd.Id()}) return bitcoind, liquidd, lightningds, peerswapd, scid, electrsd, lwk } + +func clnclnLWKLiquidSetup(t *testing.T, fundAmt uint64) (*testframework.BitcoinNode, + *testframework.LiquidNode, []*CLightningNodeWithLiquid, string, + *testframework.Electrs, *testframework.LWK) { + /// Get PeerSwap plugin path and test dir + _, filename, _, _ := runtime.Caller(0) + pathToPlugin := filepath.Join(filename, "..", "..", "out", "test-builds", "peerswap") + testDir := t.TempDir() + + // Setup nodes (1 bitcoind, 1 liquidd, 2 lightningd) + bitcoind, err := testframework.NewBitcoinNode(testDir, 1) + if err != nil { + t.Fatalf("could not create bitcoind %v", err) + } + t.Cleanup(bitcoind.Kill) + + liquidd, err := testframework.NewLiquidNode(testDir, bitcoind, 1) + if err != nil { + t.Fatal("error creating liquidd node", err) + } + t.Cleanup(liquidd.Kill) + + electrsd, err := testframework.NewElectrs(testDir, 1, liquidd) + if err != nil { + t.Fatal("error creating electrsd node", err) + } + t.Cleanup(electrsd.Process.Kill) + + lwk, err := testframework.NewLWK(testDir, 1, electrsd) + if err != nil { + t.Fatal("error creating electrsd node", err) + } + t.Cleanup(lwk.Process.Kill) + + var lightningds []*testframework.CLightningNode + + lightningd, err := testframework.NewCLightningNode(testDir, bitcoind, 1) + if err != nil { + t.Fatalf("could not create liquidd %v", err) + } + t.Cleanup(lightningd.Kill) + defer printFailedFiltered(t, lightningd.DaemonProcess) + + // Create policy file and accept all peers + err = os.MkdirAll(filepath.Join(lightningd.GetDataDir(), "peerswap"), os.ModePerm) + if err != nil { + t.Fatal("could not create dir", err) + } + err = os.WriteFile(filepath.Join(lightningd.GetDataDir(), "peerswap", "policy.conf"), []byte("accept_all_peers=1\n"), os.ModePerm) + if err != nil { + t.Fatal("could not create policy file", err) + } + + walletName := "swapElements" + + // Create config file + fileConf := struct { + Liquid struct { + RpcUser string + RpcPassword string + RpcHost string + RpcPort uint + RpcWallet string + } + }{ + Liquid: struct { + RpcUser string + RpcPassword string + RpcHost string + RpcPort uint + RpcWallet string + }{ + RpcUser: liquidd.RpcUser, + RpcPassword: liquidd.RpcPassword, + RpcHost: "http://127.0.0.1", + RpcPort: uint(liquidd.RpcPort), + RpcWallet: walletName, + }, + } + + data, err := toml.Marshal(fileConf) + require.NoError(t, err) + + configPath := filepath.Join(lightningd.GetDataDir(), "peerswap", "peerswap.conf") + os.WriteFile( + configPath, + data, + os.ModePerm, + ) + + // Use lightningd with --developer turned on + lightningd.WithCmd("lightningd") + + // Add plugin to cmd line options + lightningd.AppendCmdLine([]string{ + "--dev-bitcoind-poll=1", + "--dev-fast-gossip", + "--large-channels", + fmt.Sprint("--plugin=", pathToPlugin), + }) + + lightningds = append(lightningds, lightningd) + + lightningd, err = testframework.NewCLightningNode(testDir, bitcoind, 2) + if err != nil { + t.Fatalf("could not create liquidd %v", err) + } + t.Cleanup(lightningd.Kill) + defer printFailedFiltered(t, lightningd.DaemonProcess) + + // Create policy file and accept all peers + err = os.MkdirAll(filepath.Join(lightningd.GetDataDir(), "peerswap"), os.ModePerm) + if err != nil { + t.Fatal("could not create dir", err) + } + err = os.WriteFile(filepath.Join(lightningd.GetDataDir(), "peerswap", "policy.conf"), []byte("accept_all_peers=1\n"), os.ModePerm) + if err != nil { + t.Fatal("could not create policy file", err) + } + + // Set wallet name + walletName2 := "swapLWK" + + // Create config file + fileConf2 := struct { + LWK struct { + SignerName string + WalletName string + LWKEndpoint string + ElectrumEndpoint string + Network string + LiquidSwaps bool + } + }{ + LWK: struct { + SignerName string + WalletName string + LWKEndpoint string + ElectrumEndpoint string + Network string + LiquidSwaps bool + }{ + WalletName: walletName2, + SignerName: walletName2 + "-" + "signer", + LiquidSwaps: true, + LWKEndpoint: lwk.RPCURL.String(), + ElectrumEndpoint: electrsd.RPCURL.String(), + Network: "liquid-regtest", + }, + } + data, err = toml.Marshal(fileConf2) + require.NoError(t, err) + + configPath = filepath.Join(lightningd.GetDataDir(), "peerswap", "peerswap.conf") + os.WriteFile( + configPath, + data, + os.ModePerm, + ) + + // Use lightningd with --developer turned on + lightningd.WithCmd("lightningd") + + // Add plugin to cmd line options + lightningd.AppendCmdLine([]string{ + "--dev-bitcoind-poll=1", + "--dev-fast-gossip", + "--large-channels", + fmt.Sprint("--plugin=", pathToPlugin), + }) + + lightningds = append(lightningds, lightningd) + + // Start nodes + err = bitcoind.Run(true) + if err != nil { + t.Fatalf("bitcoind.Run() got err %v", err) + } + + err = liquidd.Run(true) + if err != nil { + t.Fatalf("Run() got err %v", err) + } + ctx := context.Background() + ctx, cancel := context.WithTimeout(ctx, testframework.TIMEOUT) + defer cancel() + require.NoError(t, electrsd.Run(ctx)) + lwk.Process.Run() + + for _, lightningd := range lightningds { + err = lightningd.Run(true, true) + if err != nil { + t.Fatalf("lightningd.Run() got err %v", err) + } + err = lightningd.WaitForLog("peerswap initialized", testframework.TIMEOUT) + if err != nil { + t.Fatalf("lightningd.WaitForLog() got err %v", err) + } + } + + // Give liquid funds to nodes to have something to swap. + for _, lightningd := range lightningds { + var result clightning.GetAddressResponse + require.NoError(t, lightningd.Rpc.Request(&clightning.LiquidGetAddress{}, &result)) + _, err = liquidd.Rpc.Call("sendtoaddress", result.LiquidAddress, 10., "", "", false, false, 1, "UNSET") + require.NoError(t, err) + _ = liquidd.GenerateBlocks(20) + require.NoError(t, + testframework.WaitFor(func() bool { + var balance clightning.GetBalanceResponse + require.NoError(t, lightningd.Rpc.Request(&clightning.LiquidGetBalance{}, &balance)) + return balance.LiquidBalance >= 1000000000 + }, testframework.TIMEOUT)) + } + + // Lock txs. + _, err = liquidd.Rpc.Call("generatetoaddress", 1, testframework.LBTC_BURN) + require.NoError(t, err) + require.NoError(t, liquidd.GenerateBlocks(20)) + + // Setup channel ([0] fundAmt(10^7) ---- 0 [1]). + scid, err := lightningds[0].OpenChannel(lightningds[1], fundAmt, 0, true, true, true) + if err != nil { + t.Fatalf("lightingds[0].OpenChannel() %v", err) + } + + // Sync peer polling + var result interface{} + err = lightningds[0].Rpc.Request(&clightning.ReloadPolicyFile{}, &result) + if err != nil { + t.Fatalf("ListPeers %v", err) + } + err = lightningds[1].Rpc.Request(&clightning.ReloadPolicyFile{}, &result) + if err != nil { + t.Fatalf("ListPeers %v", err) + } + + syncPoll(&clnPollableNode{lightningds[0]}, &clnPollableNode{lightningds[1]}) + + return bitcoind, liquidd, []*CLightningNodeWithLiquid{{lightningds[0]}, {lightningds[1]}}, scid, electrsd, lwk +}