From adfb0fa60244138471760699f349e4c80cca27a6 Mon Sep 17 00:00:00 2001 From: Dmitry Holodov <787418+dimalinux@users.noreply.github.com> Date: Wed, 29 Jun 2022 12:36:03 -0500 Subject: [PATCH] Test cleanup (#135) * version independent monerod path with localhost-only binding * fixed lint target and install script * created 3rd ETH key constant to avoid race condition in tests * updated run-integration-tests.sh with the same changes made to run-unit-tests.sh * new design for paritioning out 2 unique keys to test packages * restored accidentally deleted tests/alice.key * removed websocket connection leaks from tests * made file paths unique between tests with better file cleanup * fix for the websocket connection leak commit * reverted increase of GenerateBlocks (didn't mean to commit that) * fixed maker/taker key that I had reversed * fixed incorrect zero-balance check * fixed comment on ClaimOrRefund * added back sample script for installing go in /usr/local/go * etchclient creation cleanup using t.Cleanup() * minor cleanup to ganache_test_keys code * initial dynamic monero-wallet-rpc implementation for tests * converted monero tests to use dynamic monero-wallet-rpc services * unit tests all using dynamic monero-wallet-rpc services * fixed 2 tests that failed after moving to dynamic monero-wallet-rpc services * fixed low balance issues in TestSwapState_NotifyClaimed Co-authored-by: noot <36753753+noot@users.noreply.github.com> --- .gitignore | 6 +- Makefile | 4 +- cmd/daemon/contract.go | 5 +- cmd/daemon/contract_test.go | 10 ++- cmd/recover/main_test.go | 7 +- cmd/utils/utils.go | 16 ++--- common/consts.go | 4 +- dleq/dleq.go | 4 +- docs/build.md | 16 +++-- docs/local.md | 22 +++--- monero/client_test.go | 36 +++++----- monero/rpc_test.go | 3 +- monero/utils.go | 2 +- monero/utils_test.go | 11 ++- net/host_test.go | 3 +- net/message/message.go | 2 +- net/utils_test.go | 6 +- protocol/backend/backend_test.go | 22 +++--- protocol/utils.go | 15 ++-- protocol/write_test.go | 6 +- protocol/xmrmaker/message_handler.go | 4 +- protocol/xmrmaker/recovery.go | 4 +- protocol/xmrmaker/recovery_test.go | 4 +- protocol/xmrmaker/swap_state.go | 14 ++-- protocol/xmrmaker/swap_state_test.go | 24 ++++--- protocol/xmrmaker/utils_test.go | 4 +- protocol/xmrtaker/instance_test.go | 4 +- protocol/xmrtaker/message_handler.go | 2 +- protocol/xmrtaker/net_test.go | 9 ++- protocol/xmrtaker/recovery.go | 7 +- protocol/xmrtaker/recovery_test.go | 4 +- protocol/xmrtaker/swap_state.go | 16 ++--- protocol/xmrtaker/swap_state_test.go | 30 +++++--- recover/recovery.go | 8 +-- recover/recovery_test.go | 56 ++++++++++----- scripts/install-lint.sh | 7 ++ scripts/install-monero-linux.sh | 14 +++- scripts/install_lint.sh | 12 ---- scripts/run-integration-tests.sh | 45 ++++++------ scripts/run-unit-tests.sh | 37 +++------- scripts/setup-devnet.sh | 24 +++---- scripts/setup-env.sh | 25 ++++--- scripts/setup-stagenet.sh | 7 +- swapfactory/swap_factory_test.go | 8 ++- tests/ganache_test_keys.go | 103 +++++++++++++++++++++++++++ tests/monero_wallet_rpc.go | 89 +++++++++++++++++++++++ 46 files changed, 518 insertions(+), 243 deletions(-) create mode 100755 scripts/install-lint.sh delete mode 100755 scripts/install_lint.sh create mode 100644 tests/ganache_test_keys.go create mode 100644 tests/monero_wallet_rpc.go diff --git a/.gitignore b/.gitignore index 5c39ffef6..4b49a0850 100644 --- a/.gitignore +++ b/.gitignore @@ -11,8 +11,8 @@ *.dylib # Monero dir -monero.tar.bz2 -/monero-x86* +/monero.tar.bz2 +/monero-* # bin swapcli @@ -52,4 +52,4 @@ Cargo.lock *.key log -*.log \ No newline at end of file +*.log diff --git a/Makefile b/Makefile index 2046f55be..27d098518 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,10 @@ .PHONY: lint test install build build-dleq mock all: build-dleq install +GOPATH ?= $(shell go env GOPATH) + lint: - ./scripts/install_lint.sh + ./scripts/install-lint.sh ${GOPATH}/bin/golangci-lint run test: diff --git a/cmd/daemon/contract.go b/cmd/daemon/contract.go index 26f8c123d..4df290931 100644 --- a/cmd/daemon/contract.go +++ b/cmd/daemon/contract.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "math/big" + "path" "github.com/noot/atomic-swap/common" pcommon "github.com/noot/atomic-swap/protocol" @@ -20,7 +21,7 @@ var ( errNoEthereumPrivateKey = errors.New("must provide --ethereum-privkey file for non-development environment") ) -func getOrDeploySwapFactory(address ethcommon.Address, env common.Environment, basepath string, chainID *big.Int, +func getOrDeploySwapFactory(address ethcommon.Address, env common.Environment, basePath string, chainID *big.Int, privkey *ecdsa.PrivateKey, ec *ethclient.Client) (*swapfactory.SwapFactory, ethcommon.Address, error) { var ( sf *swapfactory.SwapFactory @@ -46,7 +47,7 @@ func getOrDeploySwapFactory(address ethcommon.Address, env common.Environment, b log.Infof("deployed SwapFactory.sol: address=%s tx hash=%s", address, tx.Hash()) // store the contract address on disk - fp := fmt.Sprintf("%s/contractaddress", basepath) + fp := path.Join(basePath, "contractaddress") if err = pcommon.WriteContractAddressToFile(fp, address.String()); err != nil { return nil, ethcommon.Address{}, fmt.Errorf("failed to write contract address to file: %w", err) } diff --git a/cmd/daemon/contract_test.go b/cmd/daemon/contract_test.go index d9d103db5..2bce091b2 100644 --- a/cmd/daemon/contract_test.go +++ b/cmd/daemon/contract_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/noot/atomic-swap/common" + "github.com/noot/atomic-swap/tests" ethcommon "github.com/ethereum/go-ethereum/common" ethcrypto "github.com/ethereum/go-ethereum/crypto" @@ -13,15 +14,18 @@ import ( ) func TestGetOrDeploySwapFactory(t *testing.T) { - pk, err := ethcrypto.HexToECDSA(common.DefaultPrivKeyXMRTaker) + pk, err := ethcrypto.HexToECDSA(tests.GetTakerTestKey(t)) require.NoError(t, err) ec, err := ethclient.Dial(common.DefaultEthEndpoint) require.NoError(t, err) + defer ec.Close() + + tmpDir := t.TempDir() _, addr, err := getOrDeploySwapFactory(ethcommon.Address{}, common.Development, - "/tmp", + tmpDir, big.NewInt(common.GanacheChainID), pk, ec, @@ -31,7 +35,7 @@ func TestGetOrDeploySwapFactory(t *testing.T) { _, addr2, err := getOrDeploySwapFactory(addr, common.Development, - "/tmp", + tmpDir, big.NewInt(common.GanacheChainID), pk, ec, diff --git a/cmd/recover/main_test.go b/cmd/recover/main_test.go index 816f8393b..3b5bd0183 100644 --- a/cmd/recover/main_test.go +++ b/cmd/recover/main_test.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "os" + "path" "strconv" "testing" @@ -16,6 +17,7 @@ import ( "github.com/noot/atomic-swap/protocol/xmrmaker" "github.com/noot/atomic-swap/protocol/xmrtaker" "github.com/noot/atomic-swap/swapfactory" + "github.com/noot/atomic-swap/tests" "github.com/stretchr/testify/require" "github.com/urfave/cli" @@ -120,7 +122,7 @@ func createInfoFile(t *testing.T, kpA, kpB *mcrypto.PrivateKeyPair, contractAddr bz, err := json.MarshalIndent(infofile, "", "\t") require.NoError(t, err) - filepath := os.TempDir() + "/test-infofile.txt" + filepath := path.Join(t.TempDir(), "test-infofile.txt") err = ioutil.WriteFile(filepath, bz, os.ModePerm) require.NoError(t, err) return filepath @@ -136,10 +138,11 @@ func TestRecover_sharedSwapSecret(t *testing.T) { c := newTestContext(t, "test --xmrtaker with shared swap secret", - []string{flagXMRTaker, flagInfoFile}, + []string{flagXMRTaker, flagInfoFile, flagMoneroWalletEndpoint}, []interface{}{ true, infoFilePath, + tests.CreateWalletRPCService(t), }, ) diff --git a/cmd/utils/utils.go b/cmd/utils/utils.go index 8bbdab5fd..4b9bb60b5 100644 --- a/cmd/utils/utils.go +++ b/cmd/utils/utils.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "strings" logging "github.com/ipfs/go-log" "github.com/urfave/cli" @@ -28,19 +29,14 @@ var ( // GetEthereumPrivateKey returns an ethereum private key hex string given the CLI options. func GetEthereumPrivateKey(c *cli.Context, env common.Environment, devXMRMaker, - useExternal bool) (ethPrivKey string, err error) { + useExternal bool) (ethPrivKeyHex string, err error) { if c.String(flagEthereumPrivKey) != "" { ethPrivKeyFile := c.String(flagEthereumPrivKey) key, err := os.ReadFile(filepath.Clean(ethPrivKeyFile)) if err != nil { return "", fmt.Errorf("failed to read ethereum-privkey file: %w", err) } - - if key[len(key)-1] == '\n' { - key = key[:len(key)-1] - } - - ethPrivKey = string(key) + ethPrivKeyHex = strings.TrimSpace(string(key)) } else { if env != common.Development || useExternal { // TODO: allow this to be set via RPC @@ -50,13 +46,13 @@ func GetEthereumPrivateKey(c *cli.Context, env common.Environment, devXMRMaker, log.Warn("no ethereum private key file provided, using ganache deterministic key") if devXMRMaker { - ethPrivKey = common.DefaultPrivKeyXMRMaker + ethPrivKeyHex = common.DefaultPrivKeyXMRMaker } else { - ethPrivKey = common.DefaultPrivKeyXMRTaker + ethPrivKeyHex = common.DefaultPrivKeyXMRTaker } } - return ethPrivKey, nil + return ethPrivKeyHex, nil } // GetEnvironment returns a common.Environment from the CLI options. diff --git a/common/consts.go b/common/consts.go index 241e05dfe..e1d93d942 100644 --- a/common/consts.go +++ b/common/consts.go @@ -10,9 +10,9 @@ const ( DefaultMoneroDaemonEndpoint = "http://127.0.0.1:18081/json_rpc" DefaultEthEndpoint = "ws://localhost:8545" - // DefaultPrivKeyXMRTaker is the private key at index 0 from `ganache-cli -d` + // DefaultPrivKeyXMRTaker is the private key at index 0 from `ganache-cli --deterministic` DefaultPrivKeyXMRTaker = "4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d" - // DefaultPrivKeyXMRMaker is the private key at index 1 from `ganache-cli -d` + // DefaultPrivKeyXMRMaker is the private key at index 1 from `ganache-cli --deterministic` DefaultPrivKeyXMRMaker = "6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1" ) diff --git a/dleq/dleq.go b/dleq/dleq.go index 364cee31a..cdf0ed97b 100644 --- a/dleq/dleq.go +++ b/dleq/dleq.go @@ -90,7 +90,7 @@ type FarcasterDLEq struct{} // Prove generates a new DLEq proof func (d *FarcasterDLEq) Prove() (*Proof, error) { - t := time.Now().Format("2006-Jan-2-15:04:05") + t := time.Now().Format("2006-01-02-15:04:05.999999999") path := fmt.Sprintf("%s-%s", defaultProofPath, t) cmd := exec.Command(dleqGenBinPath, path) @@ -119,7 +119,7 @@ func (d *FarcasterDLEq) Prove() (*Proof, error) { // Verify verifies a DLEq proof func (d *FarcasterDLEq) Verify(p *Proof) (*VerifyResult, error) { - t := time.Now().Format("2006-Jan-2-15:04:05") + t := time.Now().Format("2006-01-02-15:04:05.999999999") path := fmt.Sprintf("%s-verify-%s", defaultProofPath, t) if err := ioutil.WriteFile(path, p.proof, os.ModePerm); err != nil { diff --git a/docs/build.md b/docs/build.md index 140297b70..e9757908c 100644 --- a/docs/build.md +++ b/docs/build.md @@ -1,11 +1,17 @@ # Building the project -1. Install go [here](https://go.dev/doc/install). +1. Install Golang -For Linux 64-bit: +On Ubuntu, the easiest way to keep up-to-date with the latest stable version of +Go is with snap: ```bash -wget https://go.dev/dl/go1.18.linux-amd64.tar.gz -rm -rf /usr/local/go && tar -C /usr/local -xzf go1.18.linux-amd64.tar.gz +sudo snap install go --classic +``` +On other systems or in docker, use the directions here: https://go.dev/doc/install. +Summary for X86-64 Linux: +```bash +wget https://go.dev/dl/go1.18.3.linux-amd64.tar.gz +rm -rf /usr/local/go && tar -C /usr/local -xzf go1.18.3.linux-amd64.tar.gz echo "export PATH=$PATH:/usr/local/go/bin" >> .profile source .profile ``` @@ -21,4 +27,4 @@ cd atomic-swap make build ``` -This creates the binaries `swapd` and `swapcli`. \ No newline at end of file +This creates the binaries `swapd` and `swapcli`. diff --git a/docs/local.md b/docs/local.md index 2aba46aed..2f7d2a2cd 100644 --- a/docs/local.md +++ b/docs/local.md @@ -3,23 +3,29 @@ ### Requirements - go 1.17+ (see [build instructions](./build.md) to download Go.) -- ganache-cli (can be installed with `npm i -g ganache-cli`) I suggest using nvm to install npm: https://github.com/nvm-sh/nvm#installing-and-updating +- ganache (can be installed with `npm install --location=global ganache-cli`) -Note: this program has only been tested on Ubuntu 20.04. +These programs and scripts have only been tested on X86-64 Ubuntu 20.04 and 22.04. +Using nvm is [the suggested way](https://github.com/nvm-sh/nvm#installing-and-updating) +to install npm. If you install npm using a package manager like snap, ensure the install +prefix (`npm config get prefix`) is a directory that you have write access to without sudo. +You can change the directory with the command `npm config set prefix ~/.npm-packages`. See +[this document](https://github.com/sindresorhus/guides/blob/main/npm-global-without-sudo.md) +if you want a more sophisticated setup. #### Set up development environment Note: the `scripts/install-monero-linux.sh` script will download the monero binaries needed for you. You can also check out the `scripts/run-unit-tests.sh` script for the commands needed to setup the environment. -Start ganache-cli with determinstic keys: +Start ganache-cli with deterministic keys: ```bash -ganache-cli -d +ganache-cli --deterministic --accounts=20 ``` Start monerod for regtest, this binary is in the monero bin directory: ```bash -cd ./monero-x86_64-linux-gnu -./monerod --regtest --fixed-difficulty=1 --rpc-bind-port 18081 --offline +cd ./monero-bin +./monerod --regtest --fixed-difficulty=1 --rpc-bind-ip 127.0.0.1 --rpc-bind-port 18081 --offline ``` Create a wallet for "Bob", who will own XMR later on: @@ -31,7 +37,7 @@ You do not need to mine blocks, and you can exit the the wallet-cli once Bob's a Start monero-wallet-rpc for Bob on port 18083. Make sure `--wallet-dir` corresponds to the directory the wallet from the previous step is in: ```bash -./monero-wallet-rpc --rpc-bind-port 18083 --password "" --disable-rpc-login --wallet-dir . +./monero-wallet-rpc --rpc-bind-ip 127.0.0.1 --rpc-bind-port 18083 --password "" --disable-rpc-login --wallet-dir . ``` Open the wallet: @@ -61,7 +67,7 @@ This will deposit some XMR in Bob's account. Start monero-wallet-rpc for Alice on port 18084 (note that the directory provided to `--wallet-dir` is where Alice's XMR wallet will end up): ```bash -./monero-wallet-rpc --rpc-bind-port 18084 --password "" --disable-rpc-login --wallet-dir . +./monero-wallet-rpc --rpc-bind-ip 127.0.0.1 --rpc-bind-port 18084 --password "" --disable-rpc-login --wallet-dir . ``` #### Build and run diff --git a/monero/client_test.go b/monero/client_test.go index af93ab198..1f3b2a3f4 100644 --- a/monero/client_test.go +++ b/monero/client_test.go @@ -1,14 +1,13 @@ package monero import ( - "crypto/rand" "fmt" - "math/big" "testing" "time" "github.com/noot/atomic-swap/common" mcrypto "github.com/noot/atomic-swap/crypto/monero" + "github.com/noot/atomic-swap/tests" "github.com/stretchr/testify/require" ) @@ -19,16 +18,16 @@ func TestClient_Transfer(t *testing.T) { } const amount = 2800000000 - cXMRMaker := NewClient(common.DefaultXMRMakerMoneroEndpoint) + cXMRMaker := NewClient(tests.CreateWalletRPCService(t)) - err := cXMRMaker.OpenWallet("test-wallet", "") + err := cXMRMaker.CreateWallet("test-wallet", "") require.NoError(t, err) xmrmakerAddr, err := cXMRMaker.callGetAddress(0) require.NoError(t, err) daemon := NewClient(common.DefaultMoneroDaemonEndpoint) - _ = daemon.callGenerateBlocks(xmrmakerAddr.Address, 181) + _ = daemon.callGenerateBlocks(xmrmakerAddr.Address, 512) time.Sleep(time.Second * 10) @@ -51,13 +50,10 @@ func TestClient_Transfer(t *testing.T) { kpABPub := mcrypto.SumSpendAndViewKeys(kpA.PublicKeyPair(), kpB.PublicKeyPair()) vkABPriv := mcrypto.SumPrivateViewKeys(kpA.ViewKey(), kpB.ViewKey()) - r, err := rand.Int(rand.Reader, big.NewInt(10000)) - require.NoError(t, err) - - cXMRTaker := NewClient(common.DefaultXMRTakerMoneroEndpoint) + cXMRTaker := NewClient(tests.CreateWalletRPCService(t)) // generate view-only account for A+B - walletFP := fmt.Sprintf("test-wallet-%d", r) + walletFP := fmt.Sprintf("test-wallet-%s", time.Now().Format("2006-01-02-15:04:05.999999999")) err = cXMRTaker.callGenerateFromKeys(nil, vkABPriv, kpABPub.Address(common.Mainnet), walletFP, "") require.NoError(t, err) err = cXMRTaker.OpenWallet(walletFP, "") @@ -84,20 +80,20 @@ func TestClient_Transfer(t *testing.T) { time.Sleep(time.Second) } - _ = daemon.callGenerateBlocks(xmrmakerAddr.Address, 16) + err = daemon.callGenerateBlocks(xmrmakerAddr.Address, 16) + require.NoError(t, err) // generate spend account for A+B skAKPriv := mcrypto.SumPrivateSpendKeys(kpA.SpendKey(), kpB.SpendKey()) // ignore the error for now, as it can error with "Wallet already exists." - _ = cXMRTaker.callGenerateFromKeys(skAKPriv, vkABPriv, kpABPub.Address(common.Mainnet), - fmt.Sprintf("test-wallet-%d", r), "") + _ = cXMRTaker.callGenerateFromKeys(skAKPriv, vkABPriv, kpABPub.Address(common.Mainnet), walletFP, "") err = cXMRTaker.refresh() require.NoError(t, err) balance, err = cXMRTaker.GetBalance(0) require.NoError(t, err) - require.NotEqual(t, 0, balance.Balance) + require.Greater(t, balance.Balance, float64(0)) // transfer from account A+B back to XMRMaker's address _, err = cXMRTaker.Transfer(mcrypto.Address(xmrmakerAddr.Address), 0, 1) @@ -105,8 +101,8 @@ func TestClient_Transfer(t *testing.T) { } func TestClient_CloseWallet(t *testing.T) { - c := NewClient(common.DefaultXMRMakerMoneroEndpoint) - err := c.OpenWallet("test-wallet", "") + c := NewClient(tests.CreateWalletRPCService(t)) + err := c.CreateWallet("test-wallet", "") require.NoError(t, err) err = c.CloseWallet() @@ -117,14 +113,18 @@ func TestClient_CloseWallet(t *testing.T) { } func TestClient_GetAccounts(t *testing.T) { - c := NewClient(common.DefaultXMRMakerMoneroEndpoint) + c := NewClient(tests.CreateWalletRPCService(t)) + err := c.CreateWallet("test-wallet", "") + require.NoError(t, err) resp, err := c.GetAccounts() require.NoError(t, err) require.Equal(t, 1, len(resp.SubaddressAccounts)) } func TestClient_GetHeight(t *testing.T) { - c := NewClient(common.DefaultXMRMakerMoneroEndpoint) + c := NewClient(tests.CreateWalletRPCService(t)) + err := c.CreateWallet("test-wallet", "") + require.NoError(t, err) resp, err := c.GetHeight() require.NoError(t, err) require.NotEqual(t, 0, resp) diff --git a/monero/rpc_test.go b/monero/rpc_test.go index 2369ecf00..c85cefa74 100644 --- a/monero/rpc_test.go +++ b/monero/rpc_test.go @@ -8,6 +8,7 @@ import ( "github.com/noot/atomic-swap/common" mcrypto "github.com/noot/atomic-swap/crypto/monero" + "github.com/noot/atomic-swap/tests" "github.com/stretchr/testify/require" ) @@ -19,7 +20,7 @@ func TestCallGenerateFromKeys(t *testing.T) { r, err := rand.Int(rand.Reader, big.NewInt(999)) require.NoError(t, err) - c := NewClient(common.DefaultXMRMakerMoneroEndpoint) + c := NewClient(tests.CreateWalletRPCService(t)) err = c.callGenerateFromKeys(kp.SpendKey(), kp.ViewKey(), kp.Address(common.Mainnet), fmt.Sprintf("test-wallet-%d", r), "") require.NoError(t, err) diff --git a/monero/utils.go b/monero/utils.go index 4df9b2717..aa608198c 100644 --- a/monero/utils.go +++ b/monero/utils.go @@ -53,7 +53,7 @@ func WaitForBlocks(client Client, count int) (uint, error) { // CreateMoneroWallet creates a monero wallet from a private keypair. func CreateMoneroWallet(name string, env common.Environment, client Client, kpAB *mcrypto.PrivateKeyPair) (mcrypto.Address, error) { - t := time.Now().Format("2006-Jan-2-15:04:05") + t := time.Now().Format("2006-01-02-15:04:05.999999999") walletName := fmt.Sprintf("%s-%s", name, t) if err := client.GenerateFromKeys(kpAB, walletName, "", env); err != nil { return "", err diff --git a/monero/utils_test.go b/monero/utils_test.go index 0887f8f37..e532bff52 100644 --- a/monero/utils_test.go +++ b/monero/utils_test.go @@ -1,34 +1,41 @@ package monero import ( + "sync" "testing" "github.com/noot/atomic-swap/common" mcrypto "github.com/noot/atomic-swap/crypto/monero" + "github.com/noot/atomic-swap/tests" "github.com/stretchr/testify/require" ) func TestWaitForBlocks(t *testing.T) { - c := NewClient(common.DefaultXMRMakerMoneroEndpoint) + c := NewClient(tests.CreateWalletRPCService(t)) + require.NoError(t, c.CreateWallet("wallet", "")) daemon := NewClient(common.DefaultMoneroDaemonEndpoint) addr, err := c.callGetAddress(0) require.NoError(t, err) + var wg sync.WaitGroup + wg.Add(1) go func() { _ = daemon.callGenerateBlocks(addr.Address, 181) + wg.Done() }() _, err = WaitForBlocks(c, 1) require.NoError(t, err) + wg.Wait() } func TestCreateMoneroWallet(t *testing.T) { kp, err := mcrypto.GenerateKeys() require.NoError(t, err) - c := NewClient(common.DefaultXMRMakerMoneroEndpoint) + c := NewClient(tests.CreateWalletRPCService(t)) addr, err := CreateMoneroWallet("create-wallet-test", common.Development, c, kp) require.NoError(t, err) require.Equal(t, kp.Address(common.Development), addr) diff --git a/net/host_test.go b/net/host_test.go index a853ea9e2..957448c38 100644 --- a/net/host_test.go +++ b/net/host_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "path" "testing" "github.com/noot/atomic-swap/common" @@ -63,7 +64,7 @@ func newHost(t *testing.T, port uint16) *host { Environment: common.Development, ChainID: common.GanacheChainID, Port: port, - KeyFile: fmt.Sprintf("/tmp/node-%d.key", port), + KeyFile: path.Join(t.TempDir(), fmt.Sprintf("node-%d.key", port)), Bootnodes: []string{}, Handler: &mockHandler{}, } diff --git a/net/message/message.go b/net/message/message.go index 59ecba910..f346dd589 100644 --- a/net/message/message.go +++ b/net/message/message.go @@ -134,7 +134,7 @@ func (m *QueryResponse) Type() Type { return QueryResponseType } -// The below messages are sawp protocol messages, exchanged after the swap has been agreed +// The below messages are swap protocol messages, exchanged after the swap has been agreed // upon by both sides. // SendKeysMessage is sent by both parties to each other to initiate the protocol diff --git a/net/utils_test.go b/net/utils_test.go index 0178760a9..2fab68c31 100644 --- a/net/utils_test.go +++ b/net/utils_test.go @@ -1,17 +1,17 @@ package net import ( - "os" "testing" "github.com/stretchr/testify/require" ) func TestGenerateAndSaveKey(t *testing.T) { - _, err := generateKey(1234, os.TempDir()) + tempDir := t.TempDir() + _, err := generateKey(1234, tempDir) require.NoError(t, err) - _, err = generateKey(1234, os.TempDir()) + _, err = generateKey(1234, tempDir) require.NoError(t, err) } diff --git a/protocol/backend/backend_test.go b/protocol/backend/backend_test.go index 9ad14097f..cdf3dc8a2 100644 --- a/protocol/backend/backend_test.go +++ b/protocol/backend/backend_test.go @@ -2,10 +2,12 @@ package backend import ( "context" + "crypto/ecdsa" "math/big" "testing" "github.com/noot/atomic-swap/common" + "github.com/noot/atomic-swap/tests" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -14,30 +16,32 @@ import ( "github.com/stretchr/testify/require" ) -const defaultXMRTakerAddress = "0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1" - func TestWaitForReceipt(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ec, err := ethclient.Dial(common.DefaultEthEndpoint) require.NoError(t, err) + defer ec.Close() - pk, err := ethcrypto.HexToECDSA(common.DefaultPrivKeyXMRTaker) + privKey, err := ethcrypto.HexToECDSA(tests.GetTakerTestKey(t)) require.NoError(t, err) - nonce, err := ec.PendingNonceAt(ctx, ethcommon.HexToAddress(defaultXMRTakerAddress)) + publicKey := privKey.Public().(*ecdsa.PublicKey) + + nonce, err := ec.PendingNonceAt(ctx, ethcrypto.PubkeyToAddress(*publicKey)) require.NoError(t, err) to := ethcommon.Address{} txInner := ðtypes.LegacyTx{ - Nonce: nonce, - To: &to, - Value: big.NewInt(99), - Gas: 21000, + Nonce: nonce, + To: &to, + Value: big.NewInt(99), + Gas: 21000, + GasPrice: big.NewInt(2000000000), } - tx, err := ethtypes.SignNewTx(pk, + tx, err := ethtypes.SignNewTx(privKey, ethtypes.LatestSignerForChainID(big.NewInt(common.GanacheChainID)), txInner, ) diff --git a/protocol/utils.go b/protocol/utils.go index 42332a647..e723aa839 100644 --- a/protocol/utils.go +++ b/protocol/utils.go @@ -2,6 +2,7 @@ package protocol import ( "fmt" + "path" "time" "github.com/noot/atomic-swap/net/message" @@ -9,17 +10,15 @@ import ( ) // GetSwapInfoFilepath returns an info file path with the current timestamp. -func GetSwapInfoFilepath(basepath string) string { - t := time.Now().Format("2006-Jan-2-15:04:05") - path := fmt.Sprintf("%s/info-%s.txt", basepath, t) - return path +func GetSwapInfoFilepath(basePath string) string { + t := time.Now().Format("2006-01-02-15:04:05.999999999") + return path.Join(basePath, t) } // GetSwapRecoveryFilepath returns an info file path with the current timestamp. -func GetSwapRecoveryFilepath(basepath string) string { - t := time.Now().Format("2006-Jan-2-15:04:05") - path := fmt.Sprintf("%s/recovery-%s.txt", basepath, t) - return path +func GetSwapRecoveryFilepath(basePath string) string { + t := time.Now().Format("2006-01-02-15:04:05.999999999") + return path.Join(basePath, fmt.Sprintf("recovery-%s.txt", t)) } // ConvertContractSwapToMsg converts a swapfactory.SwapFactorySwap to a *message.ContractSwap diff --git a/protocol/write_test.go b/protocol/write_test.go index c78a465b0..f0938858c 100644 --- a/protocol/write_test.go +++ b/protocol/write_test.go @@ -1,7 +1,7 @@ package protocol import ( - "os" + "path" "testing" "github.com/noot/atomic-swap/common" @@ -14,12 +14,12 @@ func TestWriteKeysToFile(t *testing.T) { kp, err := mcrypto.GenerateKeys() require.NoError(t, err) - err = WriteKeysToFile(os.TempDir()+"/test.keys", kp, common.Development) + err = WriteKeysToFile(path.Join(t.TempDir(), "test.keys"), kp, common.Development) require.NoError(t, err) } func TestWriteContractAddrssToFile(t *testing.T) { addr := "0xabcd" - err := WriteContractAddressToFile(os.TempDir()+"/test.keys", addr) + err := WriteContractAddressToFile(path.Join(t.TempDir(), "test.keys"), addr) require.NoError(t, err) } diff --git a/protocol/xmrmaker/message_handler.go b/protocol/xmrmaker/message_handler.go index 7f126d908..9fc695b89 100644 --- a/protocol/xmrmaker/message_handler.go +++ b/protocol/xmrmaker/message_handler.go @@ -149,7 +149,7 @@ func (s *swapState) handleNotifyETHLocked(msg *message.NotifyETHLocked) (net.Mes s.contractSwapID = msg.ContractSwapID s.contractSwap = convertContractSwap(msg.ContractSwap) - if err := pcommon.WriteContractSwapToFile(s.infofile, s.contractSwapID, s.contractSwap); err != nil { + if err := pcommon.WriteContractSwapToFile(s.infoFile, s.contractSwapID, s.contractSwap); err != nil { return nil, err } @@ -162,7 +162,7 @@ func (s *swapState) handleNotifyETHLocked(msg *message.NotifyETHLocked) (net.Mes return nil, fmt.Errorf("failed to instantiate contract instance: %w", err) } - if err := pcommon.WriteContractAddressToFile(s.infofile, msg.Address); err != nil { + if err := pcommon.WriteContractAddressToFile(s.infoFile, msg.Address); err != nil { return nil, fmt.Errorf("failed to write contract address to file: %w", err) } diff --git a/protocol/xmrmaker/recovery.go b/protocol/xmrmaker/recovery.go index 627600050..ea14e8b48 100644 --- a/protocol/xmrmaker/recovery.go +++ b/protocol/xmrmaker/recovery.go @@ -19,7 +19,7 @@ type recoveryState struct { // NewRecoveryState returns a new *xmrmaker.recoveryState, // which has methods to either claim ether or reclaim monero from an initiated swap. -func NewRecoveryState(b backend.Backend, basepath string, secret *mcrypto.PrivateSpendKey, +func NewRecoveryState(b backend.Backend, basePath string, secret *mcrypto.PrivateSpendKey, contractAddr ethcommon.Address, contractSwapID [32]byte, contractSwap swapfactory.SwapFactorySwap) (*recoveryState, error) { //nolint:revive kp, err := secret.AsPrivateKeyPair() @@ -42,7 +42,7 @@ func NewRecoveryState(b backend.Backend, basepath string, secret *mcrypto.Privat dleqProof: dleq.NewProofWithSecret(sc), contractSwapID: contractSwapID, contractSwap: contractSwap, - infofile: pcommon.GetSwapRecoveryFilepath(basepath), + infoFile: pcommon.GetSwapRecoveryFilepath(basePath), } if err := s.setContract(contractAddr); err != nil { diff --git a/protocol/xmrmaker/recovery_test.go b/protocol/xmrmaker/recovery_test.go index 20a6107d2..a286ce73d 100644 --- a/protocol/xmrmaker/recovery_test.go +++ b/protocol/xmrmaker/recovery_test.go @@ -2,6 +2,7 @@ package xmrmaker import ( "math/big" + "path" "testing" "time" @@ -23,7 +24,8 @@ func newTestRecoveryState(t *testing.T) *recoveryState { require.NoError(t, err) newSwap(t, s, [32]byte{}, sr, big.NewInt(1), duration) - rs, err := NewRecoveryState(inst.backend, "/tmp/test-infofile", s.privkeys.SpendKey(), s.ContractAddr(), + basePath := path.Join(t.TempDir(), "test-infofile") + rs, err := NewRecoveryState(inst.backend, basePath, s.privkeys.SpendKey(), s.ContractAddr(), s.contractSwapID, s.contractSwap) require.NoError(t, err) diff --git a/protocol/xmrmaker/swap_state.go b/protocol/xmrmaker/swap_state.go index 6ef320f4c..64db364f9 100644 --- a/protocol/xmrmaker/swap_state.go +++ b/protocol/xmrmaker/swap_state.go @@ -43,7 +43,7 @@ type swapState struct { ctx context.Context cancel context.CancelFunc sync.Mutex - infofile string + infoFile string info *pswap.Info offer *types.Offer @@ -77,7 +77,7 @@ type swapState struct { moneroReclaimAddress mcrypto.Address } -func newSwapState(b backend.Backend, offer *types.Offer, om *offerManager, statusCh chan types.Status, infofile string, +func newSwapState(b backend.Backend, offer *types.Offer, om *offerManager, statusCh chan types.Status, infoFile string, providesAmount common.MoneroAmount, desiredAmount common.EtherAmount) (*swapState, error) { exchangeRate := types.ExchangeRate(providesAmount.AsMonero() / desiredAmount.AsEther()) stage := types.ExpectingKeys @@ -98,7 +98,7 @@ func newSwapState(b backend.Backend, offer *types.Offer, om *offerManager, statu Backend: b, offer: offer, offerManager: om, - infofile: infofile, + infoFile: infoFile, nextExpectedMessage: &net.SendKeysMessage{}, readyCh: make(chan struct{}), info: info, @@ -125,9 +125,9 @@ func (s *swapState) SendKeysMessage() (*net.SendKeysMessage, error) { }, nil } -// InfoFile returns the swap's infofile path +// InfoFile returns the swap's infoFile path func (s *swapState) InfoFile() string { - return s.infofile + return s.infoFile } // ReceivedAmount returns the amount received, or expected to be received, at the end of the swap @@ -261,7 +261,7 @@ func (s *swapState) reclaimMonero(skA *mcrypto.PrivateSpendKey) (mcrypto.Address kpAB := mcrypto.NewPrivateKeyPair(skAB, vkAB) // write keys to file in case something goes wrong - if err = pcommon.WriteSharedSwapKeyPairToFile(s.infofile, kpAB, s.Env()); err != nil { + if err = pcommon.WriteSharedSwapKeyPairToFile(s.infoFile, kpAB, s.Env()); err != nil { return "", err } @@ -361,7 +361,7 @@ func (s *swapState) generateAndSetKeys() error { s.privkeys = keysAndProof.PrivateKeyPair s.pubkeys = keysAndProof.PublicKeyPair - return pcommon.WriteKeysToFile(s.infofile, s.privkeys, s.Env()) + return pcommon.WriteKeysToFile(s.infoFile, s.privkeys, s.Env()) } func generateKeys() (*pcommon.KeysAndProof, error) { diff --git a/protocol/xmrmaker/swap_state_test.go b/protocol/xmrmaker/swap_state_test.go index ea4d3b554..a94addc38 100644 --- a/protocol/xmrmaker/swap_state_test.go +++ b/protocol/xmrmaker/swap_state_test.go @@ -4,18 +4,20 @@ import ( "context" "encoding/hex" "math/big" - "os" + "path" "testing" "time" "github.com/noot/atomic-swap/common" "github.com/noot/atomic-swap/common/types" + "github.com/noot/atomic-swap/monero" "github.com/noot/atomic-swap/net" "github.com/noot/atomic-swap/net/message" pcommon "github.com/noot/atomic-swap/protocol" "github.com/noot/atomic-swap/protocol/backend" pswap "github.com/noot/atomic-swap/protocol/swap" "github.com/noot/atomic-swap/swapfactory" + "github.com/noot/atomic-swap/tests" "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" @@ -25,8 +27,6 @@ import ( "github.com/stretchr/testify/require" ) -var infofile = os.TempDir() + "/test.keys" - var ( _ = logging.SetLogLevel("xmrmaker", "debug") testWallet = "test-wallet" @@ -47,11 +47,14 @@ var ( ) func newTestXMRMaker(t *testing.T) *Instance { - pk, err := ethcrypto.HexToECDSA(common.DefaultPrivKeyXMRMaker) + pk, err := ethcrypto.HexToECDSA(tests.GetMakerTestKey(t)) require.NoError(t, err) ec, err := ethclient.Dial(common.DefaultEthEndpoint) require.NoError(t, err) + t.Cleanup(func() { + ec.Close() + }) txOpts, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(common.GanacheChainID)) require.NoError(t, err) @@ -61,12 +64,12 @@ func newTestXMRMaker(t *testing.T) *Instance { bcfg := &backend.Config{ Ctx: context.Background(), - MoneroWalletEndpoint: common.DefaultXMRMakerMoneroEndpoint, + MoneroWalletEndpoint: tests.CreateWalletRPCService(t), MoneroDaemonEndpoint: common.DefaultMoneroDaemonEndpoint, EthereumClient: ec, EthereumPrivateKey: pk, Environment: common.Development, - ChainID: big.NewInt(common.MainnetConfig.EthereumChainID), + ChainID: big.NewInt(common.DevelopmentConfig.EthereumChainID), SwapContract: contract, SwapContractAddress: addr, SwapManager: pswap.NewManager(), @@ -78,11 +81,15 @@ func newTestXMRMaker(t *testing.T) *Instance { cfg := &Config{ Backend: b, - Basepath: "/tmp/xmrmaker", + Basepath: path.Join(t.TempDir(), "xmrmaker"), WalletFile: testWallet, WalletPassword: "", } + // NewInstance(..) below expects a pre-existing wallet, so create it + err = monero.NewClient(bcfg.MoneroWalletEndpoint).CreateWallet(cfg.WalletFile, "") + require.NoError(t, err) + xmrmaker, err := NewInstance(cfg) require.NoError(t, err) @@ -97,7 +104,8 @@ func newTestXMRMaker(t *testing.T) *Instance { func newTestInstance(t *testing.T) (*Instance, *swapState) { xmrmaker := newTestXMRMaker(t) - swapState, err := newSwapState(xmrmaker.backend, &types.Offer{}, xmrmaker.offerManager, nil, infofile, + infoFile := path.Join(t.TempDir(), "test.keys") + swapState, err := newSwapState(xmrmaker.backend, &types.Offer{}, xmrmaker.offerManager, nil, infoFile, common.MoneroAmount(33), desiredAmount) require.NoError(t, err) swapState.SetContract(xmrmaker.backend.Contract()) diff --git a/protocol/xmrmaker/utils_test.go b/protocol/xmrmaker/utils_test.go index 48a82db15..99b0868d6 100644 --- a/protocol/xmrmaker/utils_test.go +++ b/protocol/xmrmaker/utils_test.go @@ -7,6 +7,7 @@ import ( "github.com/noot/atomic-swap/common" "github.com/noot/atomic-swap/swapfactory" + "github.com/noot/atomic-swap/tests" "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" @@ -23,8 +24,9 @@ func TestCheckContractCode(t *testing.T) { ec, err := ethclient.Dial(common.DefaultEthEndpoint) require.NoError(t, err) + defer ec.Close() - pk, err := ethcrypto.HexToECDSA(common.DefaultPrivKeyXMRMaker) + pk, err := ethcrypto.HexToECDSA(tests.GetMakerTestKey(t)) require.NoError(t, err) txOpts, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(common.GanacheChainID)) diff --git a/protocol/xmrtaker/instance_test.go b/protocol/xmrtaker/instance_test.go index 03ba7e510..9647a05d0 100644 --- a/protocol/xmrtaker/instance_test.go +++ b/protocol/xmrtaker/instance_test.go @@ -3,14 +3,14 @@ package xmrtaker import ( "testing" - "github.com/noot/atomic-swap/common" "github.com/noot/atomic-swap/monero" + "github.com/noot/atomic-swap/tests" "github.com/stretchr/testify/require" ) func TestGetAddress(t *testing.T) { - c := monero.NewClient(common.DefaultXMRTakerMoneroEndpoint) + c := monero.NewClient(tests.CreateWalletRPCService(t)) addr, err := getAddress(c, "", "") require.NoError(t, err) diff --git a/protocol/xmrtaker/message_handler.go b/protocol/xmrtaker/message_handler.go index 11fbd49e4..d9dd36292 100644 --- a/protocol/xmrtaker/message_handler.go +++ b/protocol/xmrtaker/message_handler.go @@ -216,7 +216,7 @@ func (s *swapState) handleNotifyXMRLock(msg *message.NotifyXMRLock) (net.Message s.LockClient() defer s.UnlockClient() - t := time.Now().Format("2006-Jan-2-15:04:05") + t := time.Now().Format("2006-01-02-15:04:05.999999999") walletName := fmt.Sprintf("xmrtaker-viewonly-wallet-%s", t) if err := s.GenerateViewOnlyWalletFromKeys(vk, kp.Address(s.Env()), walletName, ""); err != nil { return nil, fmt.Errorf("failed to generate view-only wallet to verify locked XMR: %w", err) diff --git a/protocol/xmrtaker/net_test.go b/protocol/xmrtaker/net_test.go index 935e44283..058f71a68 100644 --- a/protocol/xmrtaker/net_test.go +++ b/protocol/xmrtaker/net_test.go @@ -1,10 +1,13 @@ package xmrtaker import ( + "path" "testing" + "github.com/noot/atomic-swap/common" "github.com/noot/atomic-swap/common/types" + "github.com/ethereum/go-ethereum/ethclient" "github.com/stretchr/testify/require" ) @@ -12,7 +15,7 @@ func newTestXMRTaker(t *testing.T) *Instance { b := newBackend(t) cfg := &Config{ Backend: b, - Basepath: "/tmp/xmrtaker", + Basepath: path.Join(t.TempDir(), "xmrtaker"), } xmrtaker, err := NewInstance(cfg) @@ -21,6 +24,10 @@ func newTestXMRTaker(t *testing.T) *Instance { } func TestXMRTaker_InitiateProtocol(t *testing.T) { + ec, err := ethclient.Dial(common.DefaultEthEndpoint) + require.NoError(t, err) + defer ec.Close() + a := newTestXMRTaker(t) offer := &types.Offer{ ExchangeRate: 1, diff --git a/protocol/xmrtaker/recovery.go b/protocol/xmrtaker/recovery.go index e3a625009..21ed30b2b 100644 --- a/protocol/xmrtaker/recovery.go +++ b/protocol/xmrtaker/recovery.go @@ -25,7 +25,7 @@ type recoveryState struct { // NewRecoveryState returns a new *xmrmaker.recoveryState, // which has methods to either claim ether or reclaim monero from an initiated swap. -func NewRecoveryState(b backend.Backend, basepath string, secret *mcrypto.PrivateSpendKey, +func NewRecoveryState(b backend.Backend, basePath string, secret *mcrypto.PrivateSpendKey, contractSwapID [32]byte, contractSwap swapfactory.SwapFactorySwap) (*recoveryState, error) { //nolint:revive kp, err := secret.AsPrivateKeyPair() if err != nil { @@ -47,7 +47,7 @@ func NewRecoveryState(b backend.Backend, basepath string, secret *mcrypto.Privat dleqProof: dleq.NewProofWithSecret(sc), contractSwapID: contractSwapID, contractSwap: contractSwap, - infofile: pcommon.GetSwapRecoveryFilepath(basepath), + infoFile: pcommon.GetSwapRecoveryFilepath(basePath), claimedCh: make(chan struct{}), } @@ -68,8 +68,7 @@ type RecoveryResult struct { MoneroAddress mcrypto.Address } -// ClaimOrRecover either claims ether or recovers monero by creating a wallet. -// It returns a *RecoveryResult. +// ClaimOrRefund either claims the monero or recovers the ether returning a *RecoveryResult. func (rs *recoveryState) ClaimOrRefund() (*RecoveryResult, error) { // check if XMRMaker claimed skA, err := rs.ss.filterForClaim() diff --git a/protocol/xmrtaker/recovery_test.go b/protocol/xmrtaker/recovery_test.go index bc79e65b2..a713281a7 100644 --- a/protocol/xmrtaker/recovery_test.go +++ b/protocol/xmrtaker/recovery_test.go @@ -1,6 +1,7 @@ package xmrtaker import ( + "path" "testing" "time" @@ -27,7 +28,8 @@ func newTestRecoveryState(t *testing.T) *recoveryState { _, err = s.lockETH(common.NewEtherAmount(1)) require.NoError(t, err) - rs, err := NewRecoveryState(s, "/tmp/test-infofile", s.privkeys.SpendKey(), s.contractSwapID, s.contractSwap) + basePath := path.Join(t.TempDir(), "test-infoFile") + rs, err := NewRecoveryState(s, basePath, s.privkeys.SpendKey(), s.contractSwapID, s.contractSwap) require.NoError(t, err) return rs } diff --git a/protocol/xmrtaker/swap_state.go b/protocol/xmrtaker/swap_state.go index c01564526..a8978f3db 100644 --- a/protocol/xmrtaker/swap_state.go +++ b/protocol/xmrtaker/swap_state.go @@ -36,7 +36,7 @@ type swapState struct { ctx context.Context cancel context.CancelFunc sync.Mutex - infofile string + infoFile string transferBack bool info *pswap.Info @@ -99,7 +99,7 @@ func newSwapState(b backend.Backend, offerID types.Hash, infofile string, transf ctx: ctx, cancel: cancel, Backend: b, - infofile: infofile, + infoFile: infofile, transferBack: transferBack, nextExpectedMessage: &net.SendKeysMessage{}, xmrLockedCh: make(chan struct{}), @@ -109,7 +109,7 @@ func newSwapState(b backend.Backend, offerID types.Hash, infofile string, transf statusCh: statusCh, } - if err := pcommon.WriteContractAddressToFile(s.infofile, b.ContractAddr().String()); err != nil { + if err := pcommon.WriteContractAddressToFile(s.infoFile, b.ContractAddr().String()); err != nil { return nil, fmt.Errorf("failed to write contract address to file: %w", err) } @@ -149,9 +149,9 @@ func (s *swapState) SendKeysMessage() (*net.SendKeysMessage, error) { }, nil } -// InfoFile returns the swap's infofile path +// InfoFile returns the swap's infoFile path func (s *swapState) InfoFile() string { - return s.infofile + return s.infoFile } // ReceivedAmount returns the amount received, or expected to be received, at the end of the swap @@ -344,7 +344,7 @@ func (s *swapState) generateAndSetKeys() error { s.privkeys = keysAndProof.PrivateKeyPair s.pubkeys = keysAndProof.PublicKeyPair - return pcommon.WriteKeysToFile(s.infofile, s.privkeys, s.Env()) + return pcommon.WriteKeysToFile(s.infoFile, s.privkeys, s.Env()) } // generateKeys generates XMRTaker's monero spend and view keys (S_b, V_b), a secp256k1 public key, @@ -419,7 +419,7 @@ func (s *swapState) lockETH(amount common.EtherAmount) (ethcommon.Hash, error) { Nonce: nonce, } - if err := pcommon.WriteContractSwapToFile(s.infofile, s.contractSwapID, s.contractSwap); err != nil { + if err := pcommon.WriteContractSwapToFile(s.infoFile, s.contractSwapID, s.contractSwap); err != nil { return ethcommon.Hash{}, err } @@ -472,7 +472,7 @@ func (s *swapState) claimMonero(skB *mcrypto.PrivateSpendKey) (mcrypto.Address, kpAB := mcrypto.NewPrivateKeyPair(skAB, vkAB) // write keys to file in case something goes wrong - if err := pcommon.WriteSharedSwapKeyPairToFile(s.infofile, kpAB, s.Env()); err != nil { + if err := pcommon.WriteSharedSwapKeyPairToFile(s.infoFile, kpAB, s.Env()); err != nil { return "", err } diff --git a/protocol/xmrtaker/swap_state_test.go b/protocol/xmrtaker/swap_state_test.go index 2883f9906..86918b902 100644 --- a/protocol/xmrtaker/swap_state_test.go +++ b/protocol/xmrtaker/swap_state_test.go @@ -21,6 +21,7 @@ import ( "github.com/noot/atomic-swap/protocol/backend" pswap "github.com/noot/atomic-swap/protocol/swap" "github.com/noot/atomic-swap/swapfactory" + "github.com/noot/atomic-swap/tests" logging "github.com/ipfs/go-log" "github.com/stretchr/testify/require" @@ -40,25 +41,28 @@ func (n *mockNet) SendSwapMessage(msg net.Message, _ types.Hash) error { } func newBackend(t *testing.T) backend.Backend { - pk, err := ethcrypto.HexToECDSA(common.DefaultPrivKeyXMRTaker) + pk, err := ethcrypto.HexToECDSA(tests.GetTakerTestKey(t)) require.NoError(t, err) ec, err := ethclient.Dial(common.DefaultEthEndpoint) require.NoError(t, err) + t.Cleanup(func() { + ec.Close() + }) - txOpts, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(common.MainnetConfig.EthereumChainID)) + txOpts, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(common.DevelopmentConfig.EthereumChainID)) require.NoError(t, err) addr, _, contract, err := swapfactory.DeploySwapFactory(txOpts, ec) require.NoError(t, err) bcfg := &backend.Config{ Ctx: context.Background(), - MoneroWalletEndpoint: common.DefaultXMRTakerMoneroEndpoint, + MoneroWalletEndpoint: tests.CreateWalletRPCService(t), MoneroDaemonEndpoint: common.DefaultMoneroDaemonEndpoint, EthereumClient: ec, EthereumPrivateKey: pk, Environment: common.Development, - ChainID: big.NewInt(common.MainnetConfig.EthereumChainID), + ChainID: big.NewInt(common.DevelopmentConfig.EthereumChainID), SwapManager: pswap.NewManager(), SwapContract: contract, SwapContractAddress: addr, @@ -71,25 +75,26 @@ func newBackend(t *testing.T) backend.Backend { } func newXMRMakerBackend(t *testing.T) backend.Backend { - pk, err := ethcrypto.HexToECDSA(common.DefaultPrivKeyXMRMaker) + pk, err := ethcrypto.HexToECDSA(tests.GetMakerTestKey(t)) require.NoError(t, err) ec, err := ethclient.Dial(common.DefaultEthEndpoint) require.NoError(t, err) + defer ec.Close() - txOpts, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(common.MainnetConfig.EthereumChainID)) + txOpts, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(common.DevelopmentConfig.EthereumChainID)) require.NoError(t, err) addr, _, contract, err := swapfactory.DeploySwapFactory(txOpts, ec) require.NoError(t, err) bcfg := &backend.Config{ Ctx: context.Background(), - MoneroWalletEndpoint: common.DefaultXMRMakerMoneroEndpoint, + MoneroWalletEndpoint: tests.CreateWalletRPCService(t), MoneroDaemonEndpoint: common.DefaultMoneroDaemonEndpoint, EthereumClient: ec, EthereumPrivateKey: pk, Environment: common.Development, - ChainID: big.NewInt(common.MainnetConfig.EthereumChainID), + ChainID: big.NewInt(common.DevelopmentConfig.EthereumChainID), SwapManager: pswap.NewManager(), SwapContract: contract, SwapContractAddress: addr, @@ -278,7 +283,7 @@ func TestSwapState_NotifyClaimed(t *testing.T) { // close swap-deposit-wallet maker := newXMRMakerBackend(t) - err := maker.OpenWallet("test-wallet", "") + err := maker.CreateWallet("test-wallet", "") require.NoError(t, err) // invalid SendKeysMessage should result in an error @@ -308,7 +313,10 @@ func TestSwapState_NotifyClaimed(t *testing.T) { require.NoError(t, err) // mine some blocks to get xmr first - _ = maker.GenerateBlocks(xmrmakerAddr.Address, 60) + err = maker.GenerateBlocks(xmrmakerAddr.Address, 60) + require.NoError(t, err) + err = maker.Refresh() + require.NoError(t, err) amt := common.MoneroAmount(1000000000) kp := mcrypto.SumSpendAndViewKeys(s.pubkeys, s.pubkeys) xmrAddr := kp.Address(common.Mainnet) @@ -331,7 +339,7 @@ func TestSwapState_NotifyClaimed(t *testing.T) { require.NotNil(t, resp) require.Equal(t, message.NotifyReadyType, resp.Type()) - _ = maker.GenerateBlocks(xmrmakerAddr.Address, 1) + err = maker.GenerateBlocks(xmrmakerAddr.Address, 1) require.NoError(t, err) // simulate xmrmaker calling claim diff --git a/recover/recovery.go b/recover/recovery.go index 61576df6a..dca99dd54 100644 --- a/recover/recovery.go +++ b/recover/recovery.go @@ -89,7 +89,7 @@ func (r *recoverer) WalletFromSharedSecret(pk *mcrypto.PrivateKeyInfo) (mcrypto. } // RecoverFromXMRMakerSecretAndContract recovers funds by either claiming ether or reclaiming locked monero. -func (r *recoverer) RecoverFromXMRMakerSecretAndContract(b backend.Backend, basepath string, +func (r *recoverer) RecoverFromXMRMakerSecretAndContract(b backend.Backend, basePath string, xmrmakerSecret, contractAddr string, swapID [32]byte, swap swapfactory.SwapFactorySwap) (*xmrmaker.RecoveryResult, error) { bs, err := hex.DecodeString(xmrmakerSecret) @@ -103,7 +103,7 @@ func (r *recoverer) RecoverFromXMRMakerSecretAndContract(b backend.Backend, base } addr := ethcommon.HexToAddress(contractAddr) - rs, err := xmrmaker.NewRecoveryState(b, basepath, bk, addr, swapID, swap) + rs, err := xmrmaker.NewRecoveryState(b, basePath, bk, addr, swapID, swap) if err != nil { return nil, err } @@ -112,7 +112,7 @@ func (r *recoverer) RecoverFromXMRMakerSecretAndContract(b backend.Backend, base } // RecoverFromXMRTakerSecretAndContract recovers funds by either claiming locked monero or refunding ether. -func (r *recoverer) RecoverFromXMRTakerSecretAndContract(b backend.Backend, basepath string, +func (r *recoverer) RecoverFromXMRTakerSecretAndContract(b backend.Backend, basePath string, xmrtakerSecret string, swapID [32]byte, swap swapfactory.SwapFactorySwap) (*xmrtaker.RecoveryResult, error) { as, err := hex.DecodeString(xmrtakerSecret) if err != nil { @@ -124,7 +124,7 @@ func (r *recoverer) RecoverFromXMRTakerSecretAndContract(b backend.Backend, base return nil, err } - rs, err := xmrtaker.NewRecoveryState(b, basepath, ak, swapID, swap) + rs, err := xmrtaker.NewRecoveryState(b, basePath, ak, swapID, swap) if err != nil { return nil, err } diff --git a/recover/recovery_test.go b/recover/recovery_test.go index ad9ecf2d9..ff80ecc33 100644 --- a/recover/recovery_test.go +++ b/recover/recovery_test.go @@ -3,6 +3,7 @@ package recovery import ( "context" "math/big" + "path" "testing" "github.com/noot/atomic-swap/common" @@ -10,6 +11,7 @@ import ( pcommon "github.com/noot/atomic-swap/protocol" "github.com/noot/atomic-swap/protocol/backend" "github.com/noot/atomic-swap/swapfactory" + "github.com/noot/atomic-swap/tests" "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" @@ -18,19 +20,28 @@ import ( "github.com/stretchr/testify/require" ) -var defaulTimeout int64 = 5 // 5 seconds +var defaultTimeout int64 = 5 // 5 seconds func newRecoverer(t *testing.T) *recoverer { - r, err := NewRecoverer(common.Development, common.DefaultXMRMakerMoneroEndpoint, common.DefaultEthEndpoint) + r, err := NewRecoverer(common.Development, tests.CreateWalletRPCService(t), common.DefaultEthEndpoint) require.NoError(t, err) return r } -func newSwap(t *testing.T, claimKey, refundKey [32]byte, - setReady bool) (ethcommon.Address, *swapfactory.SwapFactory, [32]byte, swapfactory.SwapFactorySwap) { - tm := big.NewInt(defaulTimeout) +func newSwap( + t *testing.T, + claimKey [32]byte, + refundKey [32]byte, + setReady bool, +) ( + ethcommon.Address, + *swapfactory.SwapFactory, + [32]byte, + swapfactory.SwapFactorySwap, +) { + tm := big.NewInt(defaultTimeout) - pk, err := ethcrypto.HexToECDSA(common.DefaultPrivKeyXMRTaker) + pk, err := ethcrypto.HexToECDSA(tests.GetTakerTestKey(t)) require.NoError(t, err) txOpts, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(common.GanacheChainID)) @@ -38,11 +49,14 @@ func newSwap(t *testing.T, claimKey, refundKey [32]byte, ec, err := ethclient.Dial(common.DefaultEthEndpoint) require.NoError(t, err) + t.Cleanup(func() { + ec.Close() + }) addr, _, contract, err := swapfactory.DeploySwapFactory(txOpts, ec) require.NoError(t, err) - pkXMRMaker, err := ethcrypto.HexToECDSA(common.DefaultPrivKeyXMRMaker) + pkXMRMaker, err := ethcrypto.HexToECDSA(tests.GetMakerTestKey(t)) require.NoError(t, err) nonce := big.NewInt(0) @@ -79,13 +93,20 @@ func newSwap(t *testing.T, claimKey, refundKey [32]byte, return addr, contract, swapID, swap } -func newBackend(t *testing.T, addr ethcommon.Address, contract *swapfactory.SwapFactory, - privkey string) backend.Backend { +func newBackend( + t *testing.T, + addr ethcommon.Address, + contract *swapfactory.SwapFactory, + privkey string, +) backend.Backend { pk, err := ethcrypto.HexToECDSA(privkey) require.NoError(t, err) ec, err := ethclient.Dial(common.DefaultEthEndpoint) require.NoError(t, err) + t.Cleanup(func() { + ec.Close() + }) cfg := &backend.Config{ Ctx: context.Background(), @@ -93,7 +114,7 @@ func newBackend(t *testing.T, addr ethcommon.Address, contract *swapfactory.Swap EthereumPrivateKey: pk, EthereumClient: ec, ChainID: big.NewInt(common.GanacheChainID), - MoneroWalletEndpoint: common.DefaultXMRTakerMoneroEndpoint, + MoneroWalletEndpoint: tests.CreateWalletRPCService(t), MoneroDaemonEndpoint: common.DefaultMoneroDaemonEndpoint, SwapContract: contract, SwapContractAddress: addr, @@ -126,10 +147,11 @@ func TestRecoverer_RecoverFromXMRMakerSecretAndContract_Claim(t *testing.T) { claimKey := keys.Secp256k1PublicKey.Keccak256() addr, contract, swapID, swap := newSwap(t, claimKey, [32]byte{}, true) - b := newBackend(t, addr, contract, common.DefaultPrivKeyXMRMaker) + b := newBackend(t, addr, contract, tests.GetMakerTestKey(t)) r := newRecoverer(t) - res, err := r.RecoverFromXMRMakerSecretAndContract(b, "/tmp/test-infofile", keys.PrivateKeyPair.SpendKey().Hex(), + basePath := path.Join(t.TempDir(), "test-infofile") + res, err := r.RecoverFromXMRMakerSecretAndContract(b, basePath, keys.PrivateKeyPair.SpendKey().Hex(), addr.String(), swapID, swap) require.NoError(t, err) require.True(t, res.Claimed) @@ -145,10 +167,11 @@ func TestRecoverer_RecoverFromXMRMakerSecretAndContract_Claim_afterTimeout(t *te claimKey := keys.Secp256k1PublicKey.Keccak256() addr, contract, swapID, swap := newSwap(t, claimKey, [32]byte{}, false) - b := newBackend(t, addr, contract, common.DefaultPrivKeyXMRMaker) + b := newBackend(t, addr, contract, tests.GetMakerTestKey(t)) r := newRecoverer(t) - res, err := r.RecoverFromXMRMakerSecretAndContract(b, "/tmp/test-infofile", keys.PrivateKeyPair.SpendKey().Hex(), + basePath := path.Join(t.TempDir(), "test-infofile") + res, err := r.RecoverFromXMRMakerSecretAndContract(b, basePath, keys.PrivateKeyPair.SpendKey().Hex(), addr.String(), swapID, swap) require.NoError(t, err) require.True(t, res.Claimed) @@ -164,10 +187,11 @@ func TestRecoverer_RecoverFromXMRTakerSecretAndContract_Refund(t *testing.T) { refundKey := keys.Secp256k1PublicKey.Keccak256() addr, contract, swapID, swap := newSwap(t, [32]byte{}, refundKey, false) - b := newBackend(t, addr, contract, common.DefaultPrivKeyXMRTaker) + b := newBackend(t, addr, contract, tests.GetTakerTestKey(t)) r := newRecoverer(t) - res, err := r.RecoverFromXMRTakerSecretAndContract(b, "/tmp/test-infofile", keys.PrivateKeyPair.SpendKey().Hex(), + basePath := path.Join(t.TempDir(), "test-infofile") + res, err := r.RecoverFromXMRTakerSecretAndContract(b, basePath, keys.PrivateKeyPair.SpendKey().Hex(), swapID, swap) require.NoError(t, err) require.True(t, res.Refunded) diff --git a/scripts/install-lint.sh b/scripts/install-lint.sh new file mode 100755 index 000000000..4172367b0 --- /dev/null +++ b/scripts/install-lint.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +GOBIN="$(go env GOPATH)/bin" + +if [[ ! -x "${GOBIN}/golangci-lint" ]]; then + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOBIN}" v1.46.2 +fi diff --git a/scripts/install-monero-linux.sh b/scripts/install-monero-linux.sh index 2f71bf863..c144f3d43 100755 --- a/scripts/install-monero-linux.sh +++ b/scripts/install-monero-linux.sh @@ -1,9 +1,17 @@ #!/bin/bash -if [[ -d "./monero-x86_64-linux-gnu-v0.17.3.2" ]]; then - echo "monero-x86_64-linux-gnu-v0.17.3.2 already installed" +arch=linux64 + +if [[ -d "monero-bin" ]]; then + echo "$(dirname $(realpath monero-bin)) already installed" exit 0 fi -curl -L https://downloads.getmonero.org/cli/linux64 > monero.tar.bz2 +set -e + +curl -L "https://downloads.getmonero.org/cli/${arch}" -o monero.tar.bz2 tar xjvf monero.tar.bz2 + +# Give the architecture and version specific release dir a fixed "monero-bin" symlink +versioned_dir="$(basename "$(tar tjf monero.tar.bz2 | head -1)")" +ln -sf "${versioned_dir}" monero-bin diff --git a/scripts/install_lint.sh b/scripts/install_lint.sh deleted file mode 100755 index 7f49d9927..000000000 --- a/scripts/install_lint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -if [[ -z "${GOPATH}" ]]; then - export GOPATH=~/go -fi - -if ! command -v golangci-lint &> /dev/null -then - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.45.0 -fi - -export PATH=$PATH:$(go env GOPATH)/bin \ No newline at end of file diff --git a/scripts/run-integration-tests.sh b/scripts/run-integration-tests.sh index dfe295ca3..e3dea18be 100755 --- a/scripts/run-integration-tests.sh +++ b/scripts/run-integration-tests.sh @@ -1,31 +1,34 @@ #!/bin/bash # install monero and run daemon and wallet RPC servers for alice and bob -bash ./scripts/install-monero-linux.sh +./scripts/install-monero-linux.sh echo "starting monerod..." -./monero-x86_64-linux-gnu-v0.17.3.2/monerod --detach --regtest --offline --fixed-difficulty=1 --rpc-bind-port 18081 & +./monero-bin/monerod --detach --regtest --offline --fixed-difficulty=1 --rpc-bind-ip 127.0.0.1 --rpc-bind-port 18081 sleep 5 echo "starting monero-wallet-rpc on port 18083..." -mkdir bob-test-keys -./monero-x86_64-linux-gnu-v0.17.3.2/monero-wallet-rpc --rpc-bind-port 18083 --disable-rpc-login --wallet-dir ./bob-test-keys &> monero-wallet-cli-bob.log & +mkdir -p bob-test-keys +./monero-bin/monero-wallet-rpc --rpc-bind-ip 127.0.0.1 --rpc-bind-port 18083 --disable-rpc-login --wallet-dir ./bob-test-keys &> monero-wallet-cli-bob.log & MONERO_WALLET_CLI_BOB_PID=$! sleep 5 curl http://localhost:18083/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"create_wallet","params":{"filename":"test-wallet","password":"","language":"English"}}' -H 'Content-Type: application/json' +echo echo "starting monero-wallet-rpc on port 18084..." -mkdir alice-test-keys -./monero-x86_64-linux-gnu-v0.17.3.2/monero-wallet-rpc --rpc-bind-port 18084 --disable-rpc-login --wallet-dir ./alice-test-keys &> monero-wallet-cli-alice.log & +mkdir -p alice-test-keys +./monero-bin/monero-wallet-rpc --rpc-bind-ip 127.0.0.1 --rpc-bind-port 18084 --disable-rpc-login --wallet-dir ./alice-test-keys &> monero-wallet-cli-alice.log & MONERO_WALLET_CLI_ALICE_PID=$! -# install ganache and run -echo "installing and starting ganache-cli..." -if ! command -v golangci-lint &> /dev/null; then - npm i -g ganache-cli +# install ganache-cli and run +GANACHE_EXEC="$(npm config get prefix)/bin/ganache-cli" +if [[ ! -x "${GANACHE_EXEC}" ]]; then + echo "installing ganache-cli" + npm install --location=global ganache-cli fi +echo "starting ganache-cli" export NODE_OPTIONS=--max_old_space_size=8192 -ganache-cli -d &> ganache-cli.log & +"${GANACHE_EXEC}" --deterministic --accounts=20 &> ganache-cli.log & GANACHE_CLI_PID=$! # wait for servers to start @@ -35,15 +38,15 @@ sleep 10 echo "starting alice, logs in ./tests/alice.log" bash scripts/build.sh ./swapd --dev-xmrtaker --libp2p-key=./tests/alice.key &> ./tests/alice.log & -ALICE_PID=$! +ALICE_SWAPD_PID=$! sleep 3 echo "starting bob, logs in ./tests/bob.log" ./swapd --dev-xmrmaker --bootnodes /ip4/127.0.0.1/tcp/9933/p2p/12D3KooWAYn1T8Lu122Pav4zAogjpeU61usLTNZpLRNh9gCqY6X2 --wallet-file test-wallet --deploy &> ./tests/bob.log & -BOB_PID=$! +BOB_SWAPD_PID=$! sleep 3 echo "starting charlie, logs in ./tests/charlie.log" ./swapd --libp2p-port 9955 --rpc-port 5003 --ws-port 8083 --bootnodes /ip4/127.0.0.1/tcp/9933/p2p/12D3KooWAYn1T8Lu122Pav4zAogjpeU61usLTNZpLRNh9gCqY6X2 --deploy &> ./tests/charlie.log & -CHARLIE_PID=$! +CHARLIE_SWAPD_PID=$! sleep 3 # run tests @@ -52,12 +55,12 @@ TESTS=integration go test ./tests -v OK=$? # kill processes -kill $MONERO_WALLET_CLI_BOB_PID -kill $MONERO_WALLET_CLI_ALICE_PID -kill $GANACHE_CLI_PID -kill $ALICE_PID -kill $BOB_PID -kill $CHARLIE_PID +kill "${MONERO_WALLET_CLI_BOB_PID}" || echo "Bob's wallet CLI was not running at end of test" +kill "${MONERO_WALLET_CLI_ALICE_PID}" || echo "Alice's wallet CLI was not running at end of test" +kill "${GANACHE_CLI_PID}" || echo "ganache-cli was not running at end of test" +kill "${ALICE_SWAPD_PID}" || echo "Alice's swapd was not running at end of test" +kill "${BOB_SWAPD_PID}" || echo "Bob's swapd was not running at end of test" +kill "${CHARLIE_SWAPD_PID}" || echo "Charlie's swapd was not running at end of test" # rm -rf ./alice-test-keys # rm -rf ./bob-test-keys -exit $OK \ No newline at end of file +exit $OK diff --git a/scripts/run-unit-tests.sh b/scripts/run-unit-tests.sh index 866506cf1..ef8706f7e 100755 --- a/scripts/run-unit-tests.sh +++ b/scripts/run-unit-tests.sh @@ -1,31 +1,20 @@ #!/bin/bash # install monero and run daemon and wallet RPC servers for alice and bob -bash ./scripts/install-monero-linux.sh +./scripts/install-monero-linux.sh echo "starting monerod..." -./monero-x86_64-linux-gnu-v0.17.3.2/monerod --detach --regtest --offline --fixed-difficulty=1 --rpc-bind-port 18081 & +./monero-bin/monerod --detach --regtest --offline --fixed-difficulty=1 --rpc-bind-ip 127.0.0.1 --rpc-bind-port 18081 sleep 5 -echo "starting monero-wallet-rpc on port 18083..." -mkdir bob-test-keys -./monero-x86_64-linux-gnu-v0.17.3.2/monero-wallet-rpc --rpc-bind-port 18083 --disable-rpc-login --wallet-dir ./bob-test-keys &> monero-wallet-cli-bob.log & -MONERO_WALLET_CLI_BOB_PID=$! - -sleep 5 -curl http://localhost:18083/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"create_wallet","params":{"filename":"test-wallet","password":"","language":"English"}}' -H 'Content-Type: application/json' - -echo "starting monero-wallet-rpc on port 18084..." -mkdir alice-test-keys -./monero-x86_64-linux-gnu-v0.17.3.2/monero-wallet-rpc --rpc-bind-port 18084 --disable-rpc-login --wallet-dir ./alice-test-keys &> monero-wallet-cli-alice.log & -MONERO_WALLET_CLI_ALICE_PID=$! - -# install ganache and run -echo "installing and starting ganache-cli..." -if ! command -v golangci-lint &> /dev/null; then - npm i -g ganache-cli +# install ganache-cli and run +GANACHE_EXEC="$(npm config get prefix)/bin/ganache-cli" +if [[ ! -x "${GANACHE_EXEC}" ]]; then + echo "installing ganache-cli" + npm install --location=global ganache-cli fi +echo "starting ganache-cli" export NODE_OPTIONS=--max_old_space_size=8192 -ganache-cli -d &> ganache-cli.log & +"${GANACHE_EXEC}" --deterministic --accounts=20 &> ganache-cli.log & GANACHE_CLI_PID=$! # wait for servers to start @@ -37,9 +26,5 @@ go test ./... -v -short -timeout=30m -covermode=atomic -coverprofile=coverage.ou OK=$? # kill processes -kill $MONERO_WALLET_CLI_BOB_PID -kill $MONERO_WALLET_CLI_ALICE_PID -kill $GANACHE_CLI_PID -# rm -rf ./alice-test-keys -# rm -rf ./bob-test-keys -exit $OK \ No newline at end of file +kill "${GANACHE_CLI_PID}" || echo "ganache-cli was not running at end of test" +exit $OK diff --git a/scripts/setup-devnet.sh b/scripts/setup-devnet.sh index c52730260..c5c2d07e5 100755 --- a/scripts/setup-devnet.sh +++ b/scripts/setup-devnet.sh @@ -1,25 +1,25 @@ #!/bin/bash # useful dir relative to this script -MONERO_DIR="../monero-x86_64-linux-gnu-v0.17.3.0" +MONERO_DIR="../monero-bin" # either a TMPDIR is set, or use /tmp LOG_DIR=${TMPDIR:-"/tmp"} ALICE_P2P_ADDRESS="12D3KooWBD82zGTFqk6Qmu5zeS6dQfiaAcn8go2QWE29HPmRX3yB" echo "cleanup" -pkill -e -f monero; -pkill -e -f ganache-cli; -killall -v swapd; -pkill -e -f swapcli; +pkill -e -f monero +pkill -e -f ganache-cli +killall -v swapd +pkill -e -f swapcli echo "start ganache-cli" -ganache-cli -d &> $LOG_DIR/ganache-cli.log & +"$(npm config get prefix)/bin/ganache-cli" --deterministic --accounts=20 &> "${LOG_DIR}/ganache-cli.log" & echo "move to $MONERO_DIR" -cd $MONERO_DIR +cd "${MONERO_DIR}" echo "starting monerod..." -./monerod --regtest --detach --fixed-difficulty=1 --rpc-bind-port 18081 --offline &> $LOG_DIR/monerod.log & +./monerod --regtest --detach --fixed-difficulty=1 --rpc-bind-port 18081 --offline &> "${LOG_DIR}/monerod.log" & echo "Zzz... 10s" sleep 10 @@ -31,19 +31,19 @@ echo "Zzz... 15s" sleep 15 echo "start monero-wallet-rpc for XMRTaker on port 18084" -./monero-wallet-rpc --rpc-bind-port 18084 --password "" --disable-rpc-login --wallet-dir . &> $LOG_DIR/alice-wallet-rpc.log & +./monero-wallet-rpc --rpc-bind-port 18084 --password "" --disable-rpc-login --wallet-dir . &> "${LOG_DIR}/alice-wallet-rpc.log" & echo "start monero-wallet-rpc for XMRMaker on port 18083" -./monero-wallet-rpc --rpc-bind-port 18083 --password "" --disable-rpc-login --wallet-dir . &> $LOG_DIR/bob-wallet-rpc.log & +./monero-wallet-rpc --rpc-bind-port 18083 --password "" --disable-rpc-login --wallet-dir . &> "${LOG_DIR}/bob-wallet-rpc.log" & echo "launch XMRTaker swapd" -../swapd --dev-xmrtaker --external-signer --contract-address 0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab &> $LOG_DIR/alice-swapd.log & +../swapd --dev-xmrtaker --external-signer --contract-address 0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab &> "${LOG_DIR}/alice-swapd.log" & echo "Zzz... 10s" sleep 10 echo "launch XMRMaker swapd" -../swapd --dev-xmrmaker --wallet-file XMRMaker --bootnodes /ip4/127.0.0.1/tcp/9933/p2p/$ALICE_P2P_ADDRESS &> $LOG_DIR/bob-swapd.log & +../swapd --dev-xmrmaker --wallet-file XMRMaker --bootnodes "/ip4/127.0.0.1/tcp/9933/p2p/${ALICE_P2P_ADDRESS}" &> "${LOG_DIR}/bob-swapd.log" & echo "Zzz... 10s" sleep 10 diff --git a/scripts/setup-env.sh b/scripts/setup-env.sh index 2944ef940..fdc6d2f96 100755 --- a/scripts/setup-env.sh +++ b/scripts/setup-env.sh @@ -1,31 +1,34 @@ #!/bin/bash # install monero and run daemon and wallet RPC servers for alice and bob -bash ./scripts/install-monero-linux.sh +./scripts/install-monero-linux.sh echo "starting monerod..." -./monero-x86_64-linux-gnu-v0.17.3.0/monerod --detach --regtest --offline --fixed-difficulty=1 --rpc-bind-port 18081 --keep-fakechain & +./monero-bin/monerod --detach --regtest --offline --fixed-difficulty=1 --rpc-bind-ip 127.0.0.1 --rpc-bind-port 18081 --keep-fakechain sleep 5 echo "starting monero-wallet-rpc on port 18083..." -mkdir bob-test-keys -./monero-x86_64-linux-gnu-v0.17.3.0/monero-wallet-rpc --rpc-bind-port 18083 --disable-rpc-login --wallet-dir ./bob-test-keys &> monero-wallet-cli-bob.log & +mkdir -p bob-test-keys +./monero-bin/monero-wallet-rpc --rpc-bind-ip 127.0.0.1 --rpc-bind-port 18083 --disable-rpc-login --wallet-dir ./bob-test-keys &> monero-wallet-cli-bob.log & MONERO_WALLET_CLI_BOB_PID=$! sleep 5 curl http://localhost:18083/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"create_wallet","params":{"filename":"test-wallet","password":"","language":"English"}}' -H 'Content-Type: application/json' +echo echo "starting monero-wallet-rpc on port 18084..." -mkdir alice-test-keys -./monero-x86_64-linux-gnu-v0.17.3.0/monero-wallet-rpc --rpc-bind-port 18084 --disable-rpc-login --wallet-dir ./alice-test-keys &> monero-wallet-cli-alice.log & +mkdir -p alice-test-keys +./monero-bin/monero-wallet-rpc --rpc-bind-ip 127.0.0.1 --rpc-bind-port 18084 --disable-rpc-login --wallet-dir ./alice-test-keys &> monero-wallet-cli-alice.log & MONERO_WALLET_CLI_ALICE_PID=$! -# install ganache and run -echo "installing and starting ganache-cli..." -if ! command -v golangci-lint &> /dev/null; then - npm i -g ganache-cli +# install ganache-cli and run +GANACHE_EXEC="$(npm config get prefix)/bin/ganache-cli" +if [[ ! -x "${GANACHE_EXEC}" ]]; then + echo "installing ganache-cli" + npm install --location=global ganache-cli fi +echo "starting ganache-cli" export NODE_OPTIONS=--max_old_space_size=8192 -ganache-cli -d &> ganache-cli.log & +"${GANACHE_EXEC}" --deterministic --accounts=20 &> ganache-cli.log & GANACHE_CLI_PID=$! # wait for servers to start diff --git a/scripts/setup-stagenet.sh b/scripts/setup-stagenet.sh index 8e074d6e6..e120c655e 100644 --- a/scripts/setup-stagenet.sh +++ b/scripts/setup-stagenet.sh @@ -2,14 +2,15 @@ bash ./scripts/install-monero-linux.sh echo "starting monerod..." -nohup ./monero-x86_64-linux-gnu-v0.17.3.2/monerod --detach --stagenet --rpc-bind-port 18081 & + +./monero-bin/monerod --detach --stagenet --rpc-bind-port 18081 & sleep 5 echo "starting monero-wallet-rpc on port 18083..." -nohup ./monero-x86_64-linux-gnu-v0.17.3.2/monero-wallet-rpc --rpc-bind-port 18083 --disable-rpc-login --wallet-dir ./bob-test-keys --stagenet --trusted-daemon +nohup ./monero-bin/monero-wallet-rpc --rpc-bind-port 18083 --disable-rpc-login --wallet-dir ./bob-test-keys --stagenet --trusted-daemon echo "starting monero-wallet-rpc on port 18084..." -nohup ./monero-x86_64-linux-gnu-v0.17.3.2/monero-wallet-rpc --rpc-bind-port 18084 --disable-rpc-login --wallet-dir ./alice-test-keys --stagenet --trusted-daemon +nohup ./monero-bin/monero-wallet-rpc --rpc-bind-port 18084 --disable-rpc-login --wallet-dir ./alice-test-keys --stagenet --trusted-daemon # open Bob's wallet (must have funds) sleep 5 diff --git a/swapfactory/swap_factory_test.go b/swapfactory/swap_factory_test.go index 03c5ae5e3..e88e501cb 100644 --- a/swapfactory/swap_factory_test.go +++ b/swapfactory/swap_factory_test.go @@ -17,6 +17,7 @@ import ( "github.com/noot/atomic-swap/common" "github.com/noot/atomic-swap/crypto/secp256k1" "github.com/noot/atomic-swap/dleq" + "github.com/noot/atomic-swap/tests" ) var defaultTimeoutDuration = big.NewInt(60) // 60 seconds @@ -24,7 +25,7 @@ var defaultTimeoutDuration = big.NewInt(60) // 60 seconds func setupXMRTakerAuth(t *testing.T) (*bind.TransactOpts, *ethclient.Client, *ecdsa.PrivateKey) { conn, err := ethclient.Dial(common.DefaultEthEndpoint) require.NoError(t, err) - pkA, err := crypto.HexToECDSA(common.DefaultPrivKeyXMRTaker) + pkA, err := crypto.HexToECDSA(tests.GetTakerTestKey(t)) require.NoError(t, err) auth, err := bind.NewKeyedTransactorWithChainID(pkA, big.NewInt(common.GanacheChainID)) require.NoError(t, err) @@ -33,6 +34,7 @@ func setupXMRTakerAuth(t *testing.T) (*bind.TransactOpts, *ethclient.Client, *ec func TestSwapFactory_NewSwap(t *testing.T) { auth, conn, _ := setupXMRTakerAuth(t) + defer conn.Close() address, tx, contract, err := DeploySwapFactory(auth, conn) require.NoError(t, err) require.NotEqual(t, ethcommon.Address{}, address) @@ -65,6 +67,7 @@ func TestSwapFactory_Claim_vec(t *testing.T) { // deploy swap contract with claim key hash auth, conn, pkA := setupXMRTakerAuth(t) + defer conn.Close() pub := pkA.Public().(*ecdsa.PublicKey) addr := crypto.PubkeyToAddress(*pub) @@ -200,6 +203,7 @@ func TestSwap_Refund_beforeT0(t *testing.T) { // deploy swap contract with refund key hash auth, conn, pkA := setupXMRTakerAuth(t) + defer conn.Close() pub := pkA.Public().(*ecdsa.PublicKey) addr := crypto.PubkeyToAddress(*pub) @@ -264,6 +268,7 @@ func TestSwap_Refund_afterT1(t *testing.T) { // deploy swap contract with refund key hash auth, conn, pkA := setupXMRTakerAuth(t) + defer conn.Close() pub := pkA.Public().(*ecdsa.PublicKey) addr := crypto.PubkeyToAddress(*pub) @@ -335,6 +340,7 @@ func TestSwap_Refund_afterT1(t *testing.T) { func TestSwap_MultipleSwaps(t *testing.T) { // test case where contract has multiple swaps happening at once auth, conn, pkA := setupXMRTakerAuth(t) + defer conn.Close() pub := pkA.Public().(*ecdsa.PublicKey) addr := crypto.PubkeyToAddress(*pub) diff --git a/tests/ganache_test_keys.go b/tests/ganache_test_keys.go new file mode 100644 index 000000000..4cfd2b64f --- /dev/null +++ b/tests/ganache_test_keys.go @@ -0,0 +1,103 @@ +package tests + +import ( + "runtime" + "strings" + "testing" +) + +/* + * Golang packages can be tested in parallel, but in our case, they are sharing a + * common ganache Ethereum simulator. To avoid transaction conflicts, we allocate + * each package listed in `testPackageNames` below 2 keys. One taker and one maker + * (or however the package chooses to use the keys). When different packages are tested + * in parallel, there is no shared global state, so we can't use any kind of pool or + * non-deterministic map traversals. + */ + +// testPackageNames is the list of packages with _test.go code that requires access to +// one or more prefunded ganache wallets. +var testPackageNames = []string{ + "cmd/daemon", + "protocol/backend", + "protocol/xmrmaker", + "protocol/xmrtaker", + "recover", + "swapfactory", +} + +const ( + ethKeysPerPackage = 2 + repoName = "github.com/noot/atomic-swap/" +) + +// `ganache-cli --deterministic --accounts=20` provides the following keys with +// 100 ETH on startup. The first 2 keys can be found in const.go and reserved +// for use in non-test files (files without the _test.go suffix). +var ganacheTestKeys = []string{ + "6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c", // ganache key #2 + "646f1ce2fdad0e6deeeb5c7e8e5543bdde65e86029e2fd9fc169899c440a7913", // ganache key #3 + "add53f9a7e588d003326d1cbf9e4a43c061aadd9bc938c843a79e7b4fd2ad743", // ganache key #4 + "395df67f0c2d2d9fe1ad08d1bc8b6627011959b79c53d7dd6a3536a33ab8a4fd", // ganache key #5 + "e485d098507f54e7733a205420dfddbe58db035fa577fc294ebd14db90767a52", // ganache key #6 + "a453611d9419d0e56f499079478fd72c37b251a94bfde4d19872c44cf65386e3", // ganache key #7 + "829e924fdf021ba3dbbc4225edfece9aca04b929d6e75613329ca6f1d31c0bb4", // ganache key #8 + "b0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773", // ganache key #9 + "77c5495fbb039eed474fc940f29955ed0531693cc9212911efd35dff0373153f", // ganache key #10 + "d99b5b29e6da2528bf458b26237a6cf8655a3e3276c1cdc0de1f98cefee81c01", // ganache key #11 + "9b9c613a36396172eab2d34d72331c8ca83a358781883a535d2941f66db07b24", // ganache key #12 + "0874049f95d55fb76916262dc70571701b5c4cc5900c0691af75f1a8a52c8268", // ganache key #13 + "21d7212f3b4e5332fd465877b64926e3532653e2798a11255a46f533852dfe46", // ganache key #14 + "47b65307d0d654fd4f786b908c04af8fface7710fc998b37d219de19c39ee58c", // ganache key #15 + "66109972a14d82dbdb6894e61f74708f26128814b3359b64f8b66565679f7299", // ganache key #16 + "2eac15546def97adc6d69ca6e28eec831189baa2533e7910755d15403a0749e8", // ganache key #17 + "2e114163041d2fb8d45f9251db259a68ee6bdbfd6d10fe1ae87c5c4bcd6ba491", // ganache key #18 + "ae9a2e131e9b359b198fa280de53ddbe2247730b881faae7af08e567e58915bd", // ganache key #19 +} + +func init() { + if len(testPackageNames)*ethKeysPerPackage > len(ganacheTestKeys) { + panic("Insufficient ganache test keys") + } +} + +// minPackageName takes a long-form package+function name (example: +// "github.com/noot/atomic-swap/protocol/xmrtaker.newBackend") and returns +// just the package name without the repository prefix ("protocol/xmrtaker"). +func minPackageName(t *testing.T, pkgAndFunc string) string { + minPkgAndFunc := strings.TrimPrefix(pkgAndFunc, repoName) + if minPkgAndFunc == pkgAndFunc { + t.Fatalf("%q does not have the repo prefix %q", pkgAndFunc, repoName) + } + // with the domain name gone, the minimal package is everything before the first period. + return strings.Split(minPkgAndFunc, ".")[0] +} + +func getPackageIndex(t *testing.T) uint { + // Determine the test package that requested the key from the call stack + pc, _, _, ok := runtime.Caller(2) // skipping this function and GetMakerTestKey/GetTakerTestKey + if !ok { + t.Fatalf("Failed to get caller info") + } + // returns the package and function name from the program counter + // example: "github.com/noot/atomic-swap/protocol/xmrtaker.newBackend" + packageName := minPackageName(t, runtime.FuncForPC(pc).Name()) + + for i, name := range testPackageNames { + if name == packageName { + return uint(i) + } + } + t.Fatalf("Package %q does not have reserved test keys", packageName) + panic("unreachable code") +} + +// GetMakerTestKey returns a unique Ethereum/ganache maker key per test package +func GetMakerTestKey(t *testing.T) string { + return ganacheTestKeys[getPackageIndex(t)*ethKeysPerPackage] +} + +// GetTakerTestKey returns a unique Ethereum/ganache taker key per test package +func GetTakerTestKey(t *testing.T) string { + return ganacheTestKeys[getPackageIndex(t)*ethKeysPerPackage+1] +} diff --git a/tests/monero_wallet_rpc.go b/tests/monero_wallet_rpc.go new file mode 100644 index 000000000..55c44b957 --- /dev/null +++ b/tests/monero_wallet_rpc.go @@ -0,0 +1,89 @@ +package tests + +import ( + "bufio" + "fmt" + "net" + "os/exec" + "path" + "runtime" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +// CreateWalletRPCService starts a monero-wallet-rpc listening on a random port for tests. The json_rpc +// URL of the started service is returned. +func CreateWalletRPCService(t *testing.T) string { + port := getFreePort(t) + walletRPCBin := getMoneroWalletRPCBin(t) + walletRPCBinArgs := getWalletRPCFlags(t, port) + cmd := exec.Command(walletRPCBin, walletRPCBinArgs...) + outPipe, err := cmd.StdoutPipe() + require.NoError(t, err) + + err = cmd.Start() + require.NoError(t, err) + t.Cleanup(func() { + _ = outPipe.Close() + _ = cmd.Process.Kill() + }) + scanner := bufio.NewScanner(outPipe) + started := false + for scanner.Scan() { + line := scanner.Text() + //t.Log(line) + if strings.HasSuffix(line, "Starting wallet RPC server") { + started = true + break + } + } + if !started { + t.Fatal("failed to start monero-wallet-rpc") + } + + // drain any additional output + go func() { + for scanner.Scan() { + //t.Log(scanner.Text()) + } + }() + + require.NoError(t, err) + return fmt.Sprintf("http://127.0.0.1:%d/json_rpc", port) +} + +// getMoneroWalletRPCBin returns the monero-wallet-rpc binary assuming it was +// installed at the top of the repo in a directory named "monero-bin". +func getMoneroWalletRPCBin(t *testing.T) string { + _, filename, _, ok := runtime.Caller(0) // this test file path + require.True(t, ok) + packageDir := path.Dir(filename) + repoBaseDir := path.Dir(packageDir) + return path.Join(repoBaseDir, "monero-bin", "monero-wallet-rpc") +} + +// getWalletRPCFlags returns the flags used when launching monero-wallet-rpc in a temporary +// test folder. +func getWalletRPCFlags(t *testing.T, port int) []string { + walletDir := t.TempDir() + return []string{ + "--rpc-bind-ip=127.0.0.1", + fmt.Sprintf("--rpc-bind-port=%d", port), + "--disable-rpc-login", + fmt.Sprintf("--log-file=%s", path.Join(walletDir, "monero-wallet-rpc.log")), + fmt.Sprintf("--wallet-dir=%s", t.TempDir()), + } +} + +// getFreePort returns an OS allocated and immediately freed port. There is nothing preventing +// something else on the system from using the port before the caller has a chance, but OS +// allocated ports are randomised to minimise this risk. +func getFreePort(t *testing.T) int { + ln, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + port := ln.Addr().(*net.TCPAddr).Port + require.NoError(t, ln.Close()) + return port +}