fix: bootnode version RPC call (#467)

This commit is contained in:
Dmitry Holodov
2023-05-08 19:19:19 -06:00
committed by GitHub
parent 3859162e58
commit 0abc9e6139
18 changed files with 254 additions and 160 deletions

View File

@@ -9,9 +9,9 @@ import (
"context"
"errors"
"fmt"
"math/big"
"net/http"
"github.com/athanorlabs/atomic-swap/common"
"github.com/athanorlabs/atomic-swap/net"
"github.com/athanorlabs/atomic-swap/rpc"
@@ -23,26 +23,27 @@ var log = logging.Logger("bootnode")
// Config provides the configuration for a bootnode.
type Config struct {
DataDir string
Bootnodes []string
HostListenIP string
Libp2pPort uint16
Libp2pKeyFile string
RPCPort uint16
EthereumChainID *big.Int
Env common.Environment
DataDir string
Bootnodes []string
HostListenIP string
Libp2pPort uint16
Libp2pKeyFile string
RPCPort uint16
}
// RunBootnode assembles and runs a bootnode instance, blocking until the node is
// shut down. Typically, shutdown happens because a signal handler cancels the
// passed in context, or when the shutdown RPC method is called.
func RunBootnode(ctx context.Context, cfg *Config) error {
chainID := common.ChainIDFromEnv(cfg.Env)
host, err := net.NewHost(&net.Config{
Ctx: ctx,
DataDir: cfg.DataDir,
Port: cfg.Libp2pPort,
KeyFile: cfg.Libp2pKeyFile,
Bootnodes: cfg.Bootnodes,
ProtocolID: fmt.Sprintf("%s/%d", net.ProtocolID, cfg.EthereumChainID.Int64()),
ProtocolID: fmt.Sprintf("%s/%d", net.ProtocolID, chainID),
ListenIP: cfg.HostListenIP,
IsRelayer: false,
IsBootnodeOnly: true,
@@ -61,9 +62,14 @@ func RunBootnode(ctx context.Context, cfg *Config) error {
}
rpcServer, err := rpc.NewServer(&rpc.Config{
Ctx: ctx,
Address: fmt.Sprintf("127.0.0.1:%d", cfg.RPCPort),
Net: host,
Ctx: ctx,
Env: cfg.Env,
Address: fmt.Sprintf("127.0.0.1:%d", cfg.RPCPort),
Net: host,
XMRTaker: nil,
XMRMaker: nil,
ProtocolBackend: nil,
RecoveryDB: nil,
Namespaces: map[string]struct{}{
rpc.DaemonNamespace: {},
rpc.NetNamespace: {},

View File

@@ -135,13 +135,13 @@ func runBootnode(c *cli.Context) error {
rpcPort := uint16(c.Uint(flagRPCPort))
return bootnode.RunBootnode(c.Context, &bootnode.Config{
DataDir: config.DataDir,
Bootnodes: config.Bootnodes,
HostListenIP: hostListenIP,
Libp2pPort: libp2pPort,
Libp2pKeyFile: libp2pKeyFile,
RPCPort: rpcPort,
EthereumChainID: config.EthereumChainID,
Env: config.Env,
DataDir: config.DataDir,
Bootnodes: config.Bootnodes,
HostListenIP: hostListenIP,
Libp2pPort: libp2pPort,
Libp2pKeyFile: libp2pKeyFile,
RPCPort: rpcPort,
})
}

69
cmd/bootnode/main_test.go Normal file
View File

@@ -0,0 +1,69 @@
// Copyright 2023 The AthanorLabs/atomic-swap Authors
// SPDX-License-Identifier: LGPL-3.0-only
package main
import (
"context"
"fmt"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/athanorlabs/atomic-swap/cliutil"
"github.com/athanorlabs/atomic-swap/common"
"github.com/athanorlabs/atomic-swap/daemon"
"github.com/athanorlabs/atomic-swap/rpcclient"
)
func getFreePort(t *testing.T) uint16 {
port, err := common.GetFreeTCPPort()
require.NoError(t, err)
return uint16(port)
}
func TestBootnode(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
rpcPort := getFreePort(t)
dataDir := t.TempDir()
flags := []string{
"bootnode",
fmt.Sprintf("--%s=dev", flagEnv),
fmt.Sprintf("--%s=debug", cliutil.FlagLogLevel),
fmt.Sprintf("--%s=%s", flagDataDir, dataDir),
fmt.Sprintf("--%s=%d", flagRPCPort, rpcPort),
fmt.Sprintf("--%s=0", flagLibp2pPort),
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
err := cliApp().RunContext(ctx, flags)
// We may want to replace context.Cancelled with nil at some point in the code
assert.ErrorIs(t, context.Canceled, err)
}()
// Ensure the bootnode fully starts before some basic sanity checks
daemon.WaitForSwapdStart(t, rpcPort)
cli := rpcclient.NewClient(ctx, fmt.Sprintf("http://127.0.0.1:%d", rpcPort))
versionResp, err := cli.Version()
require.NoError(t, err)
require.NotEmpty(t, versionResp.P2PVersion)
t.Logf("Bootnode p2p version is: %s", versionResp.P2PVersion)
require.Nil(t, versionResp.SwapCreatorAddr) // bootnode does not know the address
addressResp, err := cli.Addresses()
require.NoError(t, err)
require.Greater(t, len(addressResp.Addrs), 1)
// We check the contract code below, but we don't need the daemon for that
cli.Shutdown()
wg.Wait()
}

View File

@@ -7,13 +7,9 @@ import (
"context"
"crypto/ecdsa"
"fmt"
"os"
"path"
"path/filepath"
"time"
"github.com/athanorlabs/atomic-swap/common"
"github.com/athanorlabs/atomic-swap/common/vjson"
contracts "github.com/athanorlabs/atomic-swap/ethereum"
"github.com/athanorlabs/atomic-swap/ethereum/extethclient"
@@ -21,23 +17,14 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
)
const (
contractAddressesFile = "contract-addresses.json"
)
var (
errNoEthPrivateKey = fmt.Errorf("must provide --%s file for non-development environment", flagEthPrivKey)
)
type contractAddresses struct {
SwapCreatorAddr ethcommon.Address `json:"swapCreatorAddr" validate:"required"`
}
func getOrDeploySwapCreator(
ctx context.Context,
swapCreatorAddr ethcommon.Address,
env common.Environment,
dataDir string,
ec extethclient.EthClient,
) (ethcommon.Address, error) {
var err error
@@ -47,7 +34,7 @@ func getOrDeploySwapCreator(
time.Sleep(10 * time.Second)
}
swapCreatorAddr, err = deploySwapCreator(ctx, ec.Raw(), ec.PrivateKey(), dataDir)
swapCreatorAddr, err = deploySwapCreator(ctx, ec.Raw(), ec.PrivateKey())
if err != nil {
return ethcommon.Address{}, fmt.Errorf("failed to deploy swap creator: %w", err)
}
@@ -68,7 +55,6 @@ func deploySwapCreator(
ctx context.Context,
ec *ethclient.Client,
privkey *ecdsa.PrivateKey,
dataDir string,
) (ethcommon.Address, error) {
if privkey == nil {
return ethcommon.Address{}, errNoEthPrivateKey
@@ -79,25 +65,5 @@ func deploySwapCreator(
return ethcommon.Address{}, err
}
// store the contract addresses on disk
err = writeContractAddressesToFile(
path.Join(dataDir, contractAddressesFile),
&contractAddresses{
SwapCreatorAddr: swapCreatorAddr,
},
)
if err != nil {
return ethcommon.Address{}, fmt.Errorf("failed to write contract address to file: %w", err)
}
return swapCreatorAddr, nil
}
// writeContractAddressesToFile writes the contract addresses to the given file
func writeContractAddressesToFile(filePath string, addresses *contractAddresses) error {
jsonData, err := vjson.MarshalIndentStruct(addresses, "", " ")
if err != nil {
return err
}
return os.WriteFile(filepath.Clean(filePath), jsonData, 0600)
}

View File

@@ -18,13 +18,11 @@ import (
func TestGetOrDeploySwapCreator_Deploy(t *testing.T) {
pk := tests.GetTakerTestKey(t)
ec := extethclient.CreateTestClient(t, pk)
tmpDir := t.TempDir()
_, err := getOrDeploySwapCreator(
context.Background(),
ethcommon.Address{},
common.Development,
tmpDir,
ec,
)
require.NoError(t, err)
@@ -33,14 +31,12 @@ func TestGetOrDeploySwapCreator_Deploy(t *testing.T) {
func TestGetOrDeploySwapCreator_Get(t *testing.T) {
pk := tests.GetTakerTestKey(t)
ec := extethclient.CreateTestClient(t, pk)
tmpDir := t.TempDir()
// deploy and get address
address, err := getOrDeploySwapCreator(
context.Background(),
ethcommon.Address{},
common.Development,
tmpDir,
ec,
)
require.NoError(t, err)
@@ -49,7 +45,6 @@ func TestGetOrDeploySwapCreator_Get(t *testing.T) {
context.Background(),
address,
common.Development,
tmpDir,
ec,
)
require.NoError(t, err)

View File

@@ -368,7 +368,6 @@ func validateOrDeployContracts(c *cli.Context, envConf *common.Config, ec exteth
c.Context,
envConf.SwapCreatorAddr,
envConf.Env,
envConf.DataDir,
ec,
)
if err != nil {

View File

@@ -5,7 +5,6 @@ package main
import (
"context"
"encoding/json"
"fmt"
"os"
"path"
@@ -78,8 +77,14 @@ func TestDaemon_DevXMRTaker(t *testing.T) {
assert.NoError(t, err)
}()
// Ensure the daemon fully started before we cancel the context
// Ensure the daemon fully before we query the contract address
daemon.WaitForSwapdStart(t, rpcPort)
cli := rpcclient.NewClient(ctx, fmt.Sprintf("http://127.0.0.1:%d", rpcPort))
versionResp, err := cli.Version()
require.NoError(t, err)
// We check the contract code below, but we don't need the daemon for that
cancel()
wg.Wait()
@@ -87,21 +92,9 @@ func TestDaemon_DevXMRTaker(t *testing.T) {
return
}
//
// Validate that --deploy created a contract address file.
// At some future point, we will ask the RPC endpoint
// what the contract addresses are instead of using this file.
//
data, err := os.ReadFile(path.Join(dataDir, contractAddressesFile))
require.NoError(t, err)
m := make(map[string]string)
require.NoError(t, json.Unmarshal(data, &m))
swapCreatorAddr, ok := m["swapCreatorAddr"]
require.True(t, ok)
ec, _ := tests.NewEthClient(t)
ecCtx := context.Background()
err = contracts.CheckSwapCreatorContractCode(ecCtx, ec, ethcommon.HexToAddress(swapCreatorAddr))
err = contracts.CheckSwapCreatorContractCode(ecCtx, ec, *versionResp.SwapCreatorAddr)
require.NoError(t, err)
}
@@ -111,7 +104,7 @@ func TestDaemon_DevXMRMaker(t *testing.T) {
ec, _ := tests.NewEthClient(t)
// We tested --deploy with the taker, so test passing the contract address here
swapCreatorAddr, err := deploySwapCreator(context.Background(), ec, key, t.TempDir())
swapCreatorAddr, err := deploySwapCreator(context.Background(), ec, key)
require.NoError(t, err)
flags := []string{
@@ -148,7 +141,7 @@ func TestDaemon_BadFlags(t *testing.T) {
ec, _ := tests.NewEthClient(t)
ctx, _ := newTestContext(t)
swapCreatorAddr, err := deploySwapCreator(ctx, ec, key, t.TempDir())
swapCreatorAddr, err := deploySwapCreator(ctx, ec, key)
require.NoError(t, err)
baseFlags := []string{

View File

@@ -36,7 +36,6 @@ type MoneroNode struct {
type Config struct {
Env Environment
DataDir string
EthereumChainID *big.Int
EthEndpoint string
MoneroNodes []*MoneroNode
SwapCreatorAddr ethcommon.Address
@@ -46,10 +45,9 @@ type Config struct {
// MainnetConfig is the mainnet ethereum and monero configuration
func MainnetConfig() *Config {
return &Config{
Env: Mainnet,
DataDir: path.Join(baseDir, "mainnet"),
EthereumChainID: big.NewInt(MainnetChainID),
EthEndpoint: "", // No mainnet default (permissionless URLs are not reliable)
Env: Mainnet,
DataDir: path.Join(baseDir, "mainnet"),
EthEndpoint: "", // No mainnet default (permissionless URLs are not reliable)
MoneroNodes: []*MoneroNode{
{
Host: "node.sethforprivacy.com",
@@ -84,10 +82,9 @@ func MainnetConfig() *Config {
// StagenetConfig is the monero stagenet and ethereum Sepolia configuration
func StagenetConfig() *Config {
return &Config{
Env: Stagenet,
DataDir: path.Join(baseDir, "stagenet"),
EthereumChainID: big.NewInt(SepoliaChainID),
EthEndpoint: "https://rpc.sepolia.org/",
Env: Stagenet,
DataDir: path.Join(baseDir, "stagenet"),
EthEndpoint: "https://rpc.sepolia.org/",
MoneroNodes: []*MoneroNode{
{
Host: "node.sethforprivacy.com",
@@ -119,10 +116,9 @@ func StagenetConfig() *Config {
// DevelopmentConfig is the monero and ethereum development environment configuration
func DevelopmentConfig() *Config {
return &Config{
Env: Development,
EthereumChainID: big.NewInt(1337),
DataDir: path.Join(baseDir, "dev"),
EthEndpoint: DefaultGanacheEndpoint,
Env: Development,
DataDir: path.Join(baseDir, "dev"),
EthEndpoint: DefaultGanacheEndpoint,
MoneroNodes: []*MoneroNode{
{
Host: "127.0.0.1",
@@ -190,3 +186,18 @@ func DefaultMoneroPortFromEnv(env Environment) uint {
panic("invalid environment")
}
}
// ChainIDFromEnv returns the expected chainID that we should find on the
// ethereum endpoint when running int the passed environment.
func ChainIDFromEnv(env Environment) *big.Int {
switch env {
case Development:
return big.NewInt(GanacheChainID)
case Stagenet:
return big.NewInt(SepoliaChainID)
case Mainnet:
return big.NewInt(MainnetChainID)
default:
panic("invalid environment")
}
}

View File

@@ -149,6 +149,7 @@ func RunSwapDaemon(ctx context.Context, conf *SwapdConfig) (err error) {
rpcServer, err := rpc.NewServer(&rpc.Config{
Ctx: ctx,
Env: conf.EnvConf.Env,
Address: fmt.Sprintf("127.0.0.1:%d", conf.RPCPort),
Net: host,
XMRTaker: xmrTaker,

View File

@@ -479,7 +479,7 @@ func TestRunSwapDaemon_RPC_Version(t *testing.T) {
require.Equal(t, conf.EnvConf.Env, versionResp.Env)
require.NotEmpty(t, versionResp.SwapdVersion)
require.Equal(t, conf.EnvConf.SwapCreatorAddr, versionResp.SwapCreatorAddr)
require.Equal(t, conf.EnvConf.SwapCreatorAddr, *versionResp.SwapCreatorAddr)
require.Equal(t, protocolVersion, versionResp.P2PVersion)
}

View File

@@ -99,11 +99,10 @@ example below:
```bash
BOOT_NODE=/ip4/127.0.0.1/udp/9933/quic-v1/p2p/12D3KooWHRi24PVZ6TBnQJHdVyewDRcKFZtYV3qmB4KQo8iMyqik
```
Now get the ethereum contract address that Alice deployed to. This can be pulled from the Alice's logs,
the file ..., or if you have `jq` installed (available via `sudo apt install jq`), you can set a
variable like this:
Now get the ethereum contract address that Alice deployed to. This can be pulled
from the Alice's logs, or from a `version` RPC request.
```bash
CONTRACT_ADDR=$(jq -r .swapCreatorAddr "${TMPDIR-/tmp}"/xmrtaker-*/contract-addresses.json)
CONTRACT_ADDR=$(./bin/swapcli version | grep '^swap creator address' | sed 's/.*: //')
```
Now start Bob's swapd instance:

View File

@@ -13,13 +13,19 @@ import (
// DaemonService handles RPC requests for swapd version, administration and (in the future) status requests.
type DaemonService struct {
stopServer func()
pb ProtocolBackend
stopServer func()
env common.Environment
swapCreatorAddr *ethcommon.Address
}
// NewDaemonService ...
func NewDaemonService(stopServer func(), pb ProtocolBackend) *DaemonService {
return &DaemonService{stopServer, pb}
// NewDaemonService creates a new daemon service. `swapCreatorAddr` is optional
// and not set by bootnodes.
func NewDaemonService(stopServer func(), env common.Environment, swapCreatorAddr *ethcommon.Address) *DaemonService {
return &DaemonService{
stopServer: stopServer,
env: env,
swapCreatorAddr: swapCreatorAddr,
}
}
// Shutdown swapd
@@ -28,19 +34,20 @@ func (s *DaemonService) Shutdown(_ *http.Request, _ *any, _ *any) error {
return nil
}
// VersionResponse ...
// VersionResponse contains the version response provided by both swapd and
// bootnodes. In the case of bootnodes, the swapCreatorAddress is nil.
type VersionResponse struct {
SwapdVersion string `json:"swapdVersion" validate:"required"`
P2PVersion string `json:"p2pVersion" validate:"required"`
Env common.Environment `json:"env" validate:"required"`
SwapCreatorAddr ethcommon.Address `json:"swapCreatorAddress" validate:"required"`
SwapCreatorAddr *ethcommon.Address `json:"swapCreatorAddress,omitempty"`
}
// Version returns version & misc info about swapd and its dependencies
func (s *DaemonService) Version(_ *http.Request, _ *any, resp *VersionResponse) error {
resp.SwapdVersion = cliutil.GetVersion()
resp.P2PVersion = fmt.Sprintf("%s/%d", net.ProtocolID, s.pb.ETHClient().ChainID())
resp.Env = s.pb.Env()
resp.SwapCreatorAddr = s.pb.SwapCreatorAddr()
resp.P2PVersion = fmt.Sprintf("%s/%d", net.ProtocolID, common.ChainIDFromEnv(s.env))
resp.Env = s.env
resp.SwapCreatorAddr = s.swapCreatorAddr
return nil
}

View File

@@ -235,7 +235,7 @@ func (*mockProtocolBackend) ETHClient() extethclient.EthClient {
}
func (*mockProtocolBackend) SwapCreatorAddr() ethcommon.Address {
panic("not implemented")
return ethcommon.Address{}
}
func (*mockProtocolBackend) TransferXMR(_ *mcrypto.Address, _ *coins.PiconeroAmount) (string, error) {

View File

@@ -22,7 +22,6 @@ import (
"github.com/gorilla/rpc/v2"
logging "github.com/ipfs/go-log"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/athanorlabs/atomic-swap/coins"
@@ -54,12 +53,13 @@ type Server struct {
// Config ...
type Config struct {
Ctx context.Context
Env common.Environment
Address string // "IP:port"
Net Net
XMRTaker XMRTaker
XMRMaker XMRMaker
ProtocolBackend ProtocolBackend
RecoveryDB RecoveryDB
XMRTaker XMRTaker // nil on bootnodes
XMRMaker XMRMaker // nil on bootnodes
ProtocolBackend ProtocolBackend // nil on bootnodes
RecoveryDB RecoveryDB // nil on bootnodes
Namespaces map[string]struct{}
IsBootnodeOnly bool
}
@@ -81,13 +81,19 @@ func NewServer(cfg *Config) (*Server, error) {
rpcServer.RegisterCodec(NewCodec(), "application/json")
serverCtx, serverCancel := context.WithCancel(cfg.Ctx)
err := rpcServer.RegisterService(NewDaemonService(serverCancel, cfg.ProtocolBackend), "daemon")
var swapCreatorAddr *ethcommon.Address
if !cfg.IsBootnodeOnly {
addr := cfg.ProtocolBackend.SwapCreatorAddr()
swapCreatorAddr = &addr
}
daemonService := NewDaemonService(serverCancel, cfg.Env, swapCreatorAddr)
err := rpcServer.RegisterService(daemonService, "daemon")
if err != nil {
return nil, err
}
var swapManager swap.Manager
if cfg.ProtocolBackend != nil {
if !cfg.IsBootnodeOnly {
swapManager = cfg.ProtocolBackend.SwapManager()
}
@@ -139,13 +145,15 @@ func NewServer(cfg *Config) (*Server, error) {
return nil, err
}
SetupMetrics(serverCtx, reg, cfg.Net, cfg.ProtocolBackend, cfg.XMRMaker)
if !cfg.IsBootnodeOnly {
SetupMetrics(serverCtx, reg, cfg.Net, cfg.ProtocolBackend, cfg.XMRMaker)
}
r := mux.NewRouter()
r.Handle("/", rpcServer)
r.Handle("/ws", wsServer)
r.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
if !cfg.IsBootnodeOnly {
r.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
}
headersOk := handlers.AllowedHeaders([]string{"content-type", "username", "password"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
originsOk := handlers.AllowedOrigins([]string{"*"})

View File

@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/athanorlabs/atomic-swap/coins"
"github.com/athanorlabs/atomic-swap/common"
"github.com/athanorlabs/atomic-swap/common/types"
"github.com/athanorlabs/atomic-swap/rpcclient/wsclient"
)
@@ -30,6 +31,7 @@ func newServer(t *testing.T) *Server {
cfg := &Config{
Ctx: ctx,
Env: common.Development,
Address: "127.0.0.1:0", // OS assigned port
Net: new(mockNet),
ProtocolBackend: newMockProtocolBackend(),

View File

@@ -21,6 +21,9 @@ fi
echo "Stopping any monero-wallet-rpc instances"
"${pkill_cmd[@]}" '/monero-wallet-rpc '
echo "Stopping any bootnode instances"
"${pkill_cmd[@]}" '/bootnode '
echo "Stopping any monerod regest instances"
if "${pkill_cmd[@]}" '/monerod .* --regtest '; then
sleep 2 # we don't want to exit the script while it is still running

View File

@@ -32,41 +32,62 @@ create-eth-keys() {
echo "${GANACHE_KEYS[${i}]}" >"${key_file}"
done
}
# This is the local multiaddr created when using ./tests/alice-libp2p.key on the default libp2p port
ALICE_MULTIADDR=/ip4/127.0.0.1/tcp/9933/p2p/12D3KooWAAxG7eTEHr2uBVw3BDMxYsxyqfKvj3qqqpRGtTfuzTuH
ALICE_LIBP2PKEY=./tests/alice-libp2p.key
LOG_LEVEL=debug
BOOTNODE_RPC_PORT=4999
ALICE_RPC_PORT=5000
BOB_RPC_PORT=5001
CHARLIE_RPC_PORT=5002
start-bootnode() {
local flags=(
"--rpc-port=${BOOTNODE_RPC_PORT}"
--env=dev
"--log-level=${LOG_LEVEL}"
)
local log_file="${SWAP_TEST_DATA_DIR}/bootnode.log"
echo "Starting bootnode, logs in ${log_file}"
./bin/bootnode "${flags[@]}" &>"${log_file}" &
local pid="${!}"
echo "${pid}" >"${SWAP_TEST_DATA_DIR}/bootnode.pid"
if wait-rpc-started "bootnode" "${pid}" "${BOOTNODE_RPC_PORT}"; then
return 0 # success
fi
echo "Failed to start bootnode"
echo "=============== Failed logs ==============="
cat "${log_file}"
echo "============================================"
stop-daemons
exit 1
}
stop-bootnode() {
stop-program "bootnode"
}
start-swapd() {
local swapd_user="${1:?}"
local rpc_port="${2:?}"
local swapd_flags=("${@:3}" "--rpc-port=${rpc_port}")
local swapd_flags=(
"${@:3}"
--env=dev
"--rpc-port=${rpc_port}"
"--log-level=${LOG_LEVEL}"
)
local log_file="${SWAP_TEST_DATA_DIR}/${swapd_user}-swapd.log"
echo "Starting ${swapd_user^}'s swapd, logs in ${SWAP_TEST_DATA_DIR}/${swapd_user}-swapd.log"
echo "Starting ${swapd_user^}'s swapd, logs in ${log_file}"
./bin/swapd "${swapd_flags[@]}" &>"${log_file}" &
local swapd_pid="${!}"
echo "${swapd_pid}" >"${SWAP_TEST_DATA_DIR}/${swapd_user}-swapd.pid"
# Wait up to 60 seconds for the daemon's port to be listening
for i in {1..60}; do
sleep 1
# Test if pid is still alive, leave loop if it is not
if ! kill -0 "${swapd_pid}" 2>/dev/null; then
break
fi
# Test if RPC port is listening, exit success if it is
if is-port-open "${rpc_port}"; then
echo "${swapd_user^}'s swapd instance is listening after ${i} seconds"
return
fi
done
if wait-rpc-started "${swapd_user}" "${swapd_pid}" "${rpc_port}"; then
return 0 # success, bypass failure code below
fi
echo "Failed to start ${swapd_user^}'s swapd"
echo "=============== Failed logs ==============="
@@ -81,32 +102,49 @@ stop-swapd() {
stop-program "${swapd_user}-swapd"
}
# Waits for up to 60 seconds for the RPC port to be listening.
# If the passed PID exits, we stop waiting. This method only
# returns success(0)/failure(1) and will not exit the script.
wait-rpc-started() {
local swapd_user="${1}"
local rpc_port="${1}"
local user="${1}"
local pid="${2}"
local port="${3}"
# Wait up to 60 seconds for the daemon's port to be listening
for i in {1..60}; do
sleep 1
# Test if pid is still alive, fail if it isn't
if ! kill -0 "${pid}" 2>/dev/null; then
return 1 # fail
fi
# Test if RPC port is listening, return success if it is
if is-port-open "${port}"; then
echo "${user^}'s instance is listening after ${i} seconds"
return 0 # success
fi
done
return 1 # fail
}
start-daemons() {
start-monerod-regtest
start-ganache
start-bootnode
local bootnode_addr
bootnode_addr="$(./bin/swapcli addresses --swapd-port ${BOOTNODE_RPC_PORT} | grep '^1:' | sed 's/.* //')"
start-swapd alice "${ALICE_RPC_PORT}" \
--dev-xmrtaker \
"--log-level=${LOG_LEVEL}" \
"--bootnodes=${bootnode_addr}" \
"--data-dir=${SWAP_TEST_DATA_DIR}/alice" \
"--libp2p-key=${ALICE_LIBP2PKEY}" \
--deploy
CONTRACT_ADDR_FILE="${SWAP_TEST_DATA_DIR}/alice/contract-addresses.json"
if [[ ! -f "${CONTRACT_ADDR_FILE}" ]]; then
echo "Failed to get Alice's deployed contract address file"
stop-daemons
exit 1
fi
SWAP_CREATOR_ADDR="$(jq -r .swapCreatorAddr "${CONTRACT_ADDR_FILE}")"
if [[ -z "${SWAP_CREATOR_ADDR}" ]]; then
local contract_addr
contract_addr="$(./bin/swapcli version | grep '^swap creator address' | sed 's/.*: //')"
if [[ -z "${contract_addr}" ]]; then
echo "Failed to get Alice's deployed contract addresses"
stop-daemons
exit 1
@@ -114,19 +152,16 @@ start-daemons() {
start-swapd bob "${BOB_RPC_PORT}" \
--dev-xmrmaker \
"--log-level=${LOG_LEVEL}" \
"--bootnodes=${bootnode_addr}" \
"--data-dir=${SWAP_TEST_DATA_DIR}/bob" \
--libp2p-port=9944 \
"--bootnodes=${ALICE_MULTIADDR}" \
"--contract-address=${SWAP_CREATOR_ADDR}"
"--contract-address=${contract_addr}"
start-swapd charlie "${CHARLIE_RPC_PORT}" \
"--env=dev" \
"--log-level=${LOG_LEVEL}" \
"--bootnodes=${bootnode_addr}" \
--data-dir "${SWAP_TEST_DATA_DIR}/charlie" \
--libp2p-port=9955 \
"--bootnodes=${ALICE_MULTIADDR}" \
"--contract-address=${SWAP_CREATOR_ADDR}" \
"--contract-address=${contract_addr}" \
"--relayer"
}
@@ -134,6 +169,7 @@ stop-daemons() {
stop-swapd charlie
stop-swapd bob
stop-swapd alice
stop-bootnode
stop-monerod-regtest
stop-ganache
}
@@ -142,14 +178,14 @@ stop-daemons() {
echo "running integration tests..."
create-eth-keys
start-daemons
TESTS=integration CONTRACT_ADDR=${SWAP_CREATOR_ADDR} go test ./tests -v -count=1 -timeout=30m
TESTS=integration go test ./tests -v -count=1 -timeout=30m
OK="${?}"
KEEP_TEST_DATA="${OK}" stop-daemons
# Cleanup test files if we succeeded
if [[ "${OK}" -eq 0 ]]; then
rm -f "${CHARLIE_ETH_KEY}"
rm -f "${SWAP_TEST_DATA_DIR}/"{alice,bob,charlie}/{contract-addresses.json,monero-wallet-rpc.log}
rm -f "${SWAP_TEST_DATA_DIR}/"{alice,bob,charlie}/monero-wallet-rpc.log
rm -f "${SWAP_TEST_DATA_DIR}/"{alice,bob,charlie}/{net,eth}.key
rm -rf "${SWAP_TEST_DATA_DIR}/"{alice,bob,charlie}/{wallet,libp2p-datastore,db}
rmdir "${SWAP_TEST_DATA_DIR}/"{alice,bob,charlie}

View File

@@ -1 +0,0 @@
d97f10b75b1d5772fa07468f20329b13489b2bf9165e57ae0fe777630830ee9405462a30fedaaa5ebe0f30d8a8c4dc8f141876cd3fc1c8b95a898ec64bc70668