mirror of
https://github.com/AthanorLabs/atomic-swap.git
synced 2026-01-07 21:34:05 -05:00
fix: bootnode version RPC call (#467)
This commit is contained in:
@@ -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: {},
|
||||
|
||||
@@ -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
69
cmd/bootnode/main_test.go
Normal 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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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{"*"})
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
d97f10b75b1d5772fa07468f20329b13489b2bf9165e57ae0fe777630830ee9405462a30fedaaa5ebe0f30d8a8c4dc8f141876cd3fc1c8b95a898ec64bc70668
|
||||
Reference in New Issue
Block a user