Skip to content

Commit

Permalink
test: add integration test for LWK
Browse files Browse the repository at this point in the history
The integration test for LWK is added to ensure
that the swap functionality works as expected
with the LWK and electrs support.

The setup functions are implemented to create the necessary test
environment, including lwk, Bitcoin and Liquid nodes,
 as well as two lightning nodes with the PeerSwap.
This setup is crucial for testing the swap-in process
in an environment that closely mimics production.

also, added Electrs and LWK structs for testing framework.
- Implement `Electrs` struct to manage electrs daemon processes
- Implement `LWK` struct to manage LWK daemon processes
- Provide constructors for both structs to
initialize with dynamic ports and configurations
- Include methods to run the processes and connect
  • Loading branch information
YusukeShimizu committed Mar 29, 2024
1 parent e16e4ec commit f6b3031
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 5 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ require (
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.13.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
Expand Down Expand Up @@ -147,7 +148,6 @@ require (
)

require (
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/lightningnetwork/lnd/clock v1.1.0 // indirect
github.com/lightningnetwork/lnd/healthcheck v1.2.2 // indirect
github.com/lightningnetwork/lnd/kvdb v1.3.1 // indirect
Expand Down
88 changes: 88 additions & 0 deletions test/lwk_cln_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package test

import (
"math"
"os"
"testing"

"github.com/elementsproject/peerswap/clightning"
"github.com/elementsproject/peerswap/swap"
"github.com/stretchr/testify/require"
)

func Test_ClnCln_LWK_SwapIn(t *testing.T) {
t.Skip("Skipping test until we have lwk and electrs support on nix")
IsIntegrationTest(t)
t.Parallel()

t.Run("claim_normal", func(t *testing.T) {
t.Parallel()
require := require.New(t)

bitcoind, liquidd, lightningds, scid := 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,
},
)
}
}()

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{}
lightningds[1].Rpc.Request(&clightning.SwapIn{SatAmt: params.swapAmt, ShortChannelId: params.scid, Asset: asset}, &response)

}()
preimageClaimTest(t, params)
})
}
172 changes: 168 additions & 4 deletions test/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func clnclnSetupWithConfig(t *testing.T, fundAmt, pushAmt uint64, clnConf []stri
os.ModePerm,
)

// Use lightningd with --developer turned on
// Use lightningd with --developer turned on
lightningd.WithCmd("lightningd")

// Add plugin to cmd line options
Expand Down Expand Up @@ -243,7 +243,7 @@ func mixedSetup(t *testing.T, fundAmt uint64, funder fundingNode) (*testframewor
os.ModePerm,
)

// Use lightningd with --developer turned on
// Use lightningd with --developer turned on
cln.WithCmd("lightningd")

// Add plugin to cmd line options
Expand Down Expand Up @@ -405,7 +405,7 @@ func clnclnElementsSetup(t *testing.T, fundAmt uint64) (*testframework.BitcoinNo
os.ModePerm,
)

// Use lightningd with --developer turned on
// Use lightningd with --developer turned on
lightningd.WithCmd("lightningd")

// Add plugin to cmd line options
Expand Down Expand Up @@ -665,7 +665,7 @@ func mixedElementsSetup(t *testing.T, fundAmt uint64, funder fundingNode) (*test
os.ModePerm,
)

// Use lightningd with --developer turned on
// Use lightningd with --developer turned on
cln.WithCmd("lightningd")

// Add plugin to cmd line options
Expand Down Expand Up @@ -799,3 +799,167 @@ func (n *LndNodeWithLiquid) GetBtcBalanceSat() (uint64, error) {
}
return r.SatAmount, nil
}

func clnclnLWKSetup(t *testing.T, fundAmt uint64) (*testframework.BitcoinNode, *testframework.LiquidNode, []*CLightningNodeWithLiquid, string) {
/// 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
for i := 1; i <= 2; i++ {
lightningd, err := testframework.NewCLightningNode(testDir, bitcoind, i)
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
walletName := fmt.Sprintf("swap%d", i)

// Create config file
fileConf := 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: walletName,
SignerName: walletName + "-" + "signer",
LiquidSwaps: true,
},
}
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)
}

// 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
lightningd.Rpc.Request(&clightning.LiquidGetAddress{}, &result)
_ = liquidd.GenerateBlocks(20)
_, err = liquidd.Rpc.Call("sendtoaddress", result.LiquidAddress, 10., "", "", false, false, 1, "UNSET")
require.NoError(t, err)
}

// Lock txs.
_, err = liquidd.Rpc.Call("generatetoaddress", 1, testframework.LBTC_BURN)
require.NoError(t, err)

// 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
}
48 changes: 48 additions & 0 deletions testframework/electrs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package testframework

import (
"context"
"fmt"
"net/url"

"github.com/checksum0/go-electrum/electrum"
)

type Electrs struct {
Process *DaemonProcess
rpcURL *url.URL
}

func NewElectrs(testDir string, id int, elements *LiquidNode) (*Electrs, error) {
rpcPort, err := GetFreePort()
if err != nil {
return nil, err
}
u, err := url.Parse(fmt.Sprintf("127.0.0.1:%d", rpcPort))
if err != nil {
return nil, err
}
cmdLine := []string{
"electrs",
"-v",
"--network=liquidregtest",
fmt.Sprintf("daemon-rpc-addr=%s:%d", elements.rpcHost, elements.rpcPort),
fmt.Sprintf("electrum-rpc-addr=:%s", u.String()),
fmt.Sprintf("cookie=%s", elements.RpcUser+":"+elements.RpcPassword),
fmt.Sprintf("daemon-dir=%s", elements.DataDir),
"- --jsonrpc-import",
}
return &Electrs{
Process: NewDaemonProcess(cmdLine, fmt.Sprintf("electrs-%d", id)),
rpcURL: u,
}, nil
}

func (e *Electrs) Run(ctx context.Context) error {
e.Process.Run()
ec, err := electrum.NewClientTCP(ctx, e.rpcURL.String())
if err != nil {
return err
}
return ec.Ping(ctx)
}
32 changes: 32 additions & 0 deletions testframework/lwk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package testframework

import (
"fmt"
"net/url"
)

type LWK struct {
Process *DaemonProcess
}

func NewLWK(testDir string, id int, electrs *Electrs) (*Electrs, error) {
rpcPort, err := GetFreePort()
if err != nil {
return nil, err
}
u, err := url.Parse(fmt.Sprintf("127.0.0.1:%d", rpcPort))
if err != nil {
return nil, err
}
cmdLine := []string{
"lwk_cli",
"--network=regtest",
fmt.Sprintf("--addr=%s", u.String()),
"server",
"start",
fmt.Sprintf("--electrum-url=%s", electrs.rpcURL.String()),
}
return &Electrs{
Process: NewDaemonProcess(cmdLine, fmt.Sprintf("lwk-%d", id)),
}, nil
}

0 comments on commit f6b3031

Please sign in to comment.