From 5a163e0f8612573479c1ea14d8f058b42d454fd8 Mon Sep 17 00:00:00 2001 From: tecnovert Date: Fri, 4 Dec 2020 01:46:01 +0200 Subject: [PATCH] basicswap-prepare tries to initialise coin wallets from Particl mnemonic Bitcoin 0.20: 'Cannot set a new HD seed while still in Initial Block Download.' Removed in 0.21 --- .cirrus.yml | 2 +- .travis.yml | 2 +- basicswap/base.py | 9 + basicswap/basicswap.py | 87 ++++++--- basicswap/chainparams.py | 8 +- basicswap/config.py | 1 + basicswap/interface_btc.py | 11 ++ basicswap/interface_part.py | 3 + basicswap/interface_xmr.py | 36 +++- basicswap/network.py | 4 +- bin/basicswap_prepare.py | 116 +++++++++--- bin/basicswap_run.py | 55 ++++-- tests/basicswap/test_other.py | 2 +- tests/basicswap/test_prepare.py | 11 +- tests/basicswap/test_reload.py | 14 +- tests/basicswap/test_reload_xmr.py | 264 ++++++++++++++++++++++++++++ tests/basicswap/test_wallet_init.py | 202 +++++++++++++++++++++ tests/basicswap/test_xmr.py | 20 +-- 18 files changed, 754 insertions(+), 93 deletions(-) create mode 100644 tests/basicswap/test_reload_xmr.py create mode 100644 tests/basicswap/test_wallet_init.py diff --git a/.cirrus.yml b/.cirrus.yml index 835ba4b..f3ea58b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -11,7 +11,7 @@ lint_task: test_task: environment: - - PART_VERSION: 0.19.1.1 + - PART_VERSION: 0.19.1.2 - BTC_VERSION: 0.20.1 - LTC_VERSION: 0.18.1 - TEST_RELOAD_PATH: $HOME/test_basicswap1/ diff --git a/.travis.yml b/.travis.yml index 6dfb7d2..04d6070 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ stages: - test env: global: - - PART_VERSION=0.19.1.1 + - PART_VERSION=0.19.1.2 - BTC_VERSION=0.20.1 - LTC_VERSION=0.18.1 - TEST_DIR=~/test_basicswap2/ diff --git a/basicswap/base.py b/basicswap/base.py index 6052164..2bb070d 100644 --- a/basicswap/base.py +++ b/basicswap/base.py @@ -49,6 +49,9 @@ class BaseApp: self.log = logging.getLogger(self.log_name) self.log.propagate = False + # Remove any existing handlers + self.log.handlers = [] + formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s') stream_stdout = logging.StreamHandler() if self.log_name != 'BasicSwap': @@ -81,6 +84,12 @@ class BaseApp: testnet_name = '' if self.chain == 'mainnet' else chainparams[coin][self.chain].get('name', self.chain) return os.path.join(datadir, testnet_name) + def getCoinIdFromName(self, coin_name): + for c, params in chainparams.items(): + if coin_name.lower() == params['name'].lower(): + return c + raise ValueError('Unknown coin: {}'.format(coin_name)) + def getTicker(self, coin_type): ticker = chainparams[coin_type]['ticker'] if self.chain == 'testnet': diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 84aeb21..dcaa737 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -551,7 +551,6 @@ class BasicSwap(BaseApp): self.coin_clients[coin]['walletrpcauth'] = (chain_client_settings['walletrpcuser'], chain_client_settings['walletrpcpassword']) else: raise ValueError('Missing XMR wallet rpc credentials.') - self.coin_clients[coin]['interface'] = self.createInterface(coin) def ci(self, coin): # coin interface return self.coin_clients[coin]['interface'] @@ -593,17 +592,17 @@ class BasicSwap(BaseApp): try: with open(pidfilepath, 'rb') as fp: datadir_pid = int(fp.read().decode('utf-8')) - assert(datadir_pid == cc['pid']) + assert(datadir_pid == cc['pid']), 'Mismatched pid' assert(os.path.exists(authcookiepath)) except Exception: time.sleep(0.5) try: if os.name != 'nt' or cc['core_version_group'] > 17: # litecoin on windows doesn't write a pid file - assert(datadir_pid == cc['pid']) + assert(datadir_pid == cc['pid']), 'Mismatched pid' with open(authcookiepath, 'rb') as fp: cc['rpcauth'] = fp.read().decode('utf-8') - except Exception: - self.log.error('Unable to read authcookie for %s, %s, datadir pid %d, daemon pid %s', str(coin), authcookiepath, datadir_pid, cc['pid']) + except Exception as e: + self.log.error('Unable to read authcookie for %s, %s, datadir pid %d, daemon pid %s. Error: %s', str(coin), authcookiepath, datadir_pid, cc['pid'], str(e)) raise ValueError('Error, terminating') def createCoinInterface(self, coin): @@ -626,6 +625,9 @@ class BasicSwap(BaseApp): self.log.info('%s Core version %d', chainparams[c]['name'].capitalize(), core_version) self.coin_clients[c]['core_version'] = core_version + if c == Coins.XMR: + self.coin_clients[c]['interface'].ensureWalletExists() + if c == Coins.PART: self.coin_clients[c]['have_spent_index'] = self.coin_clients[c]['interface'].haveSpentIndex() @@ -636,6 +638,8 @@ class BasicSwap(BaseApp): self.initialise() def stopDaemon(self, coin): + if coin == Coins.XMR: + return num_tries = 10 authcookiepath = os.path.join(self.getChainDatadirPath(coin), '.cookie') stopping = False @@ -697,6 +701,19 @@ class BasicSwap(BaseApp): if synced < 1.0: raise ValueError('{} chain is still syncing, currently at {}.'.format(self.coin_clients[c]['name'], synced)) + def initialiseWallet(self, coin_type): + ci = self.ci(coin_type) + self.log.info('Initialising {} wallet.'.format(ci.coin_name())) + + if coin_type == Coins.XMR: + key_view = self.getWalletKey(coin_type, 1, for_ed25519=True) + key_spend = self.getWalletKey(coin_type, 2, for_ed25519=True) + ci.initialiseWallet(key_view, key_spend) + return + + root_key = self.getWalletKey(coin_type, 1) + ci.initialiseWallet(root_key) + def setIntKV(self, str_key, int_val): session = scoped_session(self.session_factory) kv = session.query(DBKVInt).filter_by(key=str_key).first() @@ -963,19 +980,8 @@ class BasicSwap(BaseApp): session.remove() self.mxDB.release() - def getPathKey(self, coin_from, coin_to, offer_created_at, contract_count, key_no, for_xmr=False): - account = self.callcoinrpc(Coins.PART, 'extkey', ['account']) - evkey = self.callcoinrpc(Coins.PART, 'extkey', ['account', 'default', 'true'])['evkey'] - ci = self.ci(coin_to) - - days = offer_created_at // 86400 - secs = offer_created_at - days * 86400 - key_path_base = '44445555h/999999/{}/{}/{}/{}/{}/{}'.format(int(coin_from), int(coin_to), days, secs, contract_count, key_no) - - if not for_xmr: - extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path_base])['key_info']['result'] - return decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey']) - + def grindForEd25519Key(self, coin_type, evkey, key_path_base): + ci = self.ci(coin_type) nonce = 1 while True: key_path = key_path_base + '/{}'.format(nonce) @@ -986,7 +992,34 @@ class BasicSwap(BaseApp): return privkey nonce += 1 if nonce > 1000: - raise ValueError('getKeyForXMR failed') + raise ValueError('grindForEd25519Key failed') + + def getWalletKey(self, coin_type, key_num, for_ed25519=False): + account = self.callcoinrpc(Coins.PART, 'extkey', ['account']) + evkey = self.callcoinrpc(Coins.PART, 'extkey', ['account', 'default', 'true'])['evkey'] + + key_path_base = '44445555h/1h/{}/{}'.format(int(coin_type), key_num) + + if not for_ed25519: + extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path_base])['key_info']['result'] + return decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey']) + + return self.grindForEd25519Key(coin_type, evkey, key_path_base) + + def getPathKey(self, coin_from, coin_to, offer_created_at, contract_count, key_no, for_ed25519=False): + account = self.callcoinrpc(Coins.PART, 'extkey', ['account']) + evkey = self.callcoinrpc(Coins.PART, 'extkey', ['account', 'default', 'true'])['evkey'] + ci = self.ci(coin_to) + + days = offer_created_at // 86400 + secs = offer_created_at - days * 86400 + key_path_base = '44445555h/999999/{}/{}/{}/{}/{}/{}'.format(int(coin_from), int(coin_to), days, secs, contract_count, key_no) + + if not for_ed25519: + extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path_base])['key_info']['result'] + return decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey']) + + return self.grindForEd25519Key(coin_to, evkey, key_path_base) def getContractPubkey(self, date, contract_count): account = self.callcoinrpc(Coins.PART, 'extkey', ['account']) @@ -1550,8 +1583,8 @@ class BasicSwap(BaseApp): xmr_swap.contract_count = self.getNewContractId() xmr_swap.dest_af = msg_buf.dest_af xmr_swap.b_restore_height = self.ci(coin_to).getChainHeight() - kbvf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 1, for_xmr=True) - kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_xmr=True) + kbvf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 1, for_ed25519=True) + kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_ed25519=True) kaf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 3) karf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 4) @@ -1660,8 +1693,8 @@ class BasicSwap(BaseApp): contract_secret = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 1) - kbvl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_xmr=True) - kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_xmr=True) + kbvl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True) + kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_ed25519=True) kal = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 4) karl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 5) @@ -3746,7 +3779,7 @@ class BasicSwap(BaseApp): ci_from = self.ci(coin_from) ci_to = self.ci(coin_to) - kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_xmr=True) + kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True) kaf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3) al_lock_spend_sig = ci_from.decryptOtVES(kbsf, xmr_swap.al_lock_spend_tx_esig) @@ -3801,7 +3834,7 @@ class BasicSwap(BaseApp): kbsf = ci_from.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, xmr_swap.al_lock_spend_tx_sig, xmr_swap.pkasf) assert(kbsf is not None) - kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_xmr=True) + kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_ed25519=True) vkbs = ci_to.sumKeys(kbsl, kbsf) address_to = ci_to.getMainWalletAddress() @@ -3835,7 +3868,7 @@ class BasicSwap(BaseApp): kbsl = ci_from.recoverEncKey(xmr_swap.af_lock_refund_spend_tx_esig, af_lock_refund_spend_tx_sig, xmr_swap.pkasl) assert(kbsl is not None) - kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_xmr=True) + kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True) vkbs = ci_to.sumKeys(kbsl, kbsf) address_to = ci_to.getMainWalletAddress() @@ -3874,7 +3907,7 @@ class BasicSwap(BaseApp): xmr_swap.af_lock_refund_spend_tx_esig = msg_data.af_lock_refund_spend_tx_esig xmr_swap.af_lock_refund_tx_sig = msg_data.af_lock_refund_tx_sig - kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_xmr=True) + kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_ed25519=True) karl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 5) xmr_swap.af_lock_refund_spend_tx_sig = ci_from.decryptOtVES(kbsl, xmr_swap.af_lock_refund_spend_tx_esig) diff --git a/basicswap/chainparams.py b/basicswap/chainparams.py index 389277f..b78f430 100644 --- a/basicswap/chainparams.py +++ b/basicswap/chainparams.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2019 tecnovert +# Copyright (c) 2019-2020 tecnovert # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. @@ -70,6 +70,7 @@ chainparams = { 'rpcport': 8332, 'pubkey_address': 0, 'script_address': 5, + 'key_prefix': 128, 'hrp': 'bc', 'bip44': 0, 'min_amount': 1000, @@ -79,6 +80,7 @@ chainparams = { 'rpcport': 18332, 'pubkey_address': 111, 'script_address': 196, + 'key_prefix': 239, 'hrp': 'tb', 'bip44': 1, 'min_amount': 1000, @@ -89,6 +91,7 @@ chainparams = { 'rpcport': 18443, 'pubkey_address': 111, 'script_address': 196, + 'key_prefix': 239, 'hrp': 'bcrt', 'bip44': 1, 'min_amount': 1000, @@ -105,6 +108,7 @@ chainparams = { 'rpcport': 9332, 'pubkey_address': 48, 'script_address': 50, + 'key_prefix': 176, 'hrp': 'ltc', 'bip44': 2, 'min_amount': 1000, @@ -114,6 +118,7 @@ chainparams = { 'rpcport': 19332, 'pubkey_address': 111, 'script_address': 58, + 'key_prefix': 239, 'hrp': 'tltc', 'bip44': 1, 'min_amount': 1000, @@ -124,6 +129,7 @@ chainparams = { 'rpcport': 19443, 'pubkey_address': 111, 'script_address': 58, + 'key_prefix': 239, 'hrp': 'rltc', 'bip44': 1, 'min_amount': 1000, diff --git a/basicswap/config.py b/basicswap/config.py index dc93626..4be7f33 100644 --- a/basicswap/config.py +++ b/basicswap/config.py @@ -8,6 +8,7 @@ import os CONFIG_FILENAME = 'basicswap.json' DEFAULT_DATADIR = '~/.basicswap' +DEFAULT_ALLOW_CORS = False TEST_DATADIRS = os.path.expanduser(os.getenv('DATADIRS', '/tmp/basicswap')) bin_suffix = ('.exe' if os.name == 'nt' else '') diff --git a/basicswap/interface_btc.py b/basicswap/interface_btc.py index ad7d477..b5d7b78 100644 --- a/basicswap/interface_btc.py +++ b/basicswap/interface_btc.py @@ -17,6 +17,7 @@ from .util import ( dumpj, format_amount, make_int, + toWIF, decodeAddress) from coincurve.keys import ( PublicKey) @@ -131,6 +132,16 @@ class BTCInterface(CoinInterface): def getBlockchainInfo(self): return self.rpc_callback('getblockchaininfo') + def initialiseWallet(self, key_bytes): + wif_prefix = chainparams[self.coin_type()][self._network]['key_prefix'] + key_wif = toWIF(wif_prefix, key_bytes) + + try: + self.rpc_callback('sethdseed', [True, key_wif]) + except Exception as e: + # < 0.21: Cannot set a new HD seed while still in Initial Block Download. + logging.error('sethdseed failed: {}'.format(str(e))) + def getWalletInfo(self): return self.rpc_callback('getwalletinfo') diff --git a/basicswap/interface_part.py b/basicswap/interface_part.py index b08f8e4..a8cbb29 100644 --- a/basicswap/interface_part.py +++ b/basicswap/interface_part.py @@ -40,3 +40,6 @@ class PARTInterface(BTCInterface): version = self.getDaemonVersion() index_info = self.rpc_callback('getinsightinfo' if int(str(version)[:2]) > 19 else 'getindexinfo') return index_info['spentindex'] + + def initialiseWallet(self, key): + raise ValueError('TODO') diff --git a/basicswap/interface_xmr.py b/basicswap/interface_xmr.py index 31ea246..159bb5a 100644 --- a/basicswap/interface_xmr.py +++ b/basicswap/interface_xmr.py @@ -65,11 +65,45 @@ class XMRInterface(CoinInterface): def setWalletFilename(self, wallet_filename): self._wallet_filename = wallet_filename + def initialiseWallet(self, key_view, key_spend, restore_height=None): + try: + self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename}) + # TODO: Check address + return # Wallet exists + except Exception as e: + pass + + try: + if restore_height is None: + restore_height = self.getChainHeight() + except Exception as e: + logging.warning('Unable to get restore_height, set to zero. Error: {}'.format(str(e))) + restore_height = 0 + + Kbv = self.getPubkey(key_view) + Kbs = self.getPubkey(key_spend) + address_b58 = xmr_util.encode_address(Kbv, Kbs) + + params = { + 'filename': self._wallet_filename, + 'address': address_b58, + 'viewkey': b2h(key_view[::-1]), + 'spendkey': b2h(key_spend[::-1]), + 'restore_height': restore_height, + } + rv = self.rpc_wallet_cb('generate_from_keys', params) + logging.info('generate_from_keys %s', dumpj(rv)) + self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename}) + + def ensureWalletExists(self): + self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename}) + def testDaemonRPC(self): self.rpc_wallet_cb('get_languages') def getDaemonVersion(self): - return self.rpc_cb('get_version')['version'] + return self.rpc_wallet_cb('get_version')['version'] + #return self.rpc_cb('get_version')['version'] def getBlockchainInfo(self): rv = {} diff --git a/basicswap/network.py b/basicswap/network.py index 388165b..e26c8b8 100644 --- a/basicswap/network.py +++ b/basicswap/network.py @@ -15,7 +15,7 @@ class Peer: class Network: - def __init__(self, network_port, network_key): - self._network_port = network_port + def __init__(self, p2p_port, network_key): + self._p2p_port = p2p_port self._network_key = network_key self._peers = [] diff --git a/bin/basicswap_prepare.py b/bin/basicswap_prepare.py index 80f9a26..030e582 100755 --- a/bin/basicswap_prepare.py +++ b/bin/basicswap_prepare.py @@ -16,6 +16,7 @@ import json import mmap import stat import gnupg +import signal import hashlib import tarfile import zipfile @@ -24,13 +25,15 @@ import platform import urllib.parse from urllib.request import urlretrieve - import basicswap.config as cfg from basicswap.rpc import ( callrpc_cli, waitForRPC, ) -from bin.basicswap_run import startDaemon +from basicswap.basicswap import BasicSwap +from basicswap.chainparams import Coins +from bin.basicswap_run import startDaemon, startXmrWalletDaemon + if platform.system() == 'Darwin': BIN_ARCH = 'osx64.tar.gz' @@ -40,7 +43,7 @@ else: BIN_ARCH = 'x86_64-linux-gnu.tar.gz' known_coins = { - 'particl': '0.19.1.1', + 'particl': '0.19.1.2', 'litecoin': '0.18.1', 'bitcoin': '0.20.1', 'namecoin': '0.18.0', @@ -52,6 +55,13 @@ logger.level = logging.DEBUG if not len(logger.handlers): logger.addHandler(logging.StreamHandler(sys.stdout)) +XMR_RPC_HOST = os.getenv('XMR_RPC_HOST', 'localhost') +BASE_XMR_RPC_PORT = os.getenv('BASE_XMR_RPC_PORT', 29798) +BASE_XMR_ZMQ_PORT = os.getenv('BASE_XMR_ZMQ_PORT', 29898) +BASE_XMR_WALLET_PORT = os.getenv('BASE_XMR_WALLET_PORT', 29998) +XMR_WALLET_RPC_USER = os.getenv('XMR_WALLET_RPC_USER', 'xmr_wallet_user') +XMR_WALLET_RPC_PWD = os.getenv('XMR_WALLET_RPC_PWD', 'xmr_wallet_pwd') + def make_reporthook(): read = 0 # Number of bytes read so far @@ -95,8 +105,6 @@ def extractCore(coin, version, settings, bin_dir, release_path): fout.write(fi.read()) fi.close() os.chmod(out_path, stat.S_IRWXU | stat.S_IXGRP | stat.S_IXOTH) - - print('member', member) return bins = [coin + 'd', coin + '-cli', coin + '-tx'] @@ -257,10 +265,40 @@ def prepareDataDir(coin, settings, data_dir, chain, particl_mnemonic): if not os.path.exists(data_dir): os.makedirs(data_dir) + if coin == 'monero': + core_conf_path = os.path.join(data_dir, coin + 'd.conf') + if os.path.exists(core_conf_path): + exitWithError('{} exists'.format(core_conf_path)) + with open(core_conf_path, 'w') as fp: + if chain == 'regtest': + fp.write('regtest=1\n') + fp.write('keep-fakechain=1\n') + fp.write('fixed-difficulty=1\n') + elif chain == 'testnet': + fp.write('testnet=1\n') + fp.write('data-dir={}\n'.format(data_dir)) + fp.write('rpc-bind-port={}\n'.format(core_settings['rpcport'])) + fp.write('rpc-bind-ip=127.0.0.1\n') + fp.write('zmq-rpc-bind-port={}\n'.format(core_settings['zmqport'])) + fp.write('zmq-rpc-bind-ip=127.0.0.1\n') + + #fp.write('zmq-rpc-bind-port={}\n'.format(core_settings['zmqport'])) + #fp.write('zmq-rpc-bind-ip=127.0.0.1\n') + wallet_conf_path = os.path.join(data_dir, coin + '_wallet.conf') + if os.path.exists(wallet_conf_path): + exitWithError('{} exists'.format(wallet_conf_path)) + with open(wallet_conf_path, 'w') as fp: + fp.write('daemon-address={}:{}\n'.format(core_settings['rpchost'], core_settings['rpcport'])) + fp.write('no-dns=1\n') + fp.write('rpc-bind-port={}\n'.format(core_settings['walletrpcport'])) + fp.write('wallet-dir={}\n'.format(os.path.join(data_dir, 'wallets'))) + fp.write('log-file={}\n'.format(os.path.join(data_dir, 'wallet.log'))) + fp.write('shared-ringdb-dir={}\n'.format(os.path.join(data_dir, 'shared-ringdb'))) + fp.write('rpc-login={}:{}\n'.format(core_settings['walletrpcuser'], core_settings['walletrpcpassword'])) + return core_conf_path = os.path.join(data_dir, coin + '.conf') if os.path.exists(core_conf_path): exitWithError('{} exists'.format(core_conf_path)) - with open(core_conf_path, 'w') as fp: if chain != 'mainnet': fp.write(chain + '=1\n') @@ -490,11 +528,14 @@ def main(): 'monero': { 'connection_type': 'rpc' if 'monero' in with_coins else 'none', 'manage_daemon': True if 'monero' in with_coins else False, - 'rpcport': 29798 + port_offset, - 'walletrpcport': 29799 + port_offset, - #'walletrpcuser': 'test' + str(node_id), - #'walletrpcpassword': 'test_pass' + str(node_id), - 'walletfile': 'basicswap', + 'manage_wallet_daemon': True if 'monero' in with_coins else False, + 'rpcport': BASE_XMR_RPC_PORT + port_offset, + 'zmqport': BASE_XMR_ZMQ_PORT + port_offset, + 'walletrpcport': BASE_XMR_WALLET_PORT + port_offset, + 'rpchost': XMR_RPC_HOST, + 'walletrpcuser': XMR_WALLET_RPC_USER, + 'walletrpcpassword': XMR_WALLET_RPC_PWD, + 'walletfile': 'swap_wallet', 'datadir': os.path.join(data_dir, 'monero'), 'bindir': os.path.join(bin_dir, 'monero'), } @@ -597,26 +638,57 @@ def main(): particl_settings = settings['chainclients']['particl'] partRpc = make_rpc_func(particl_settings['bindir'], particl_settings['datadir'], chain) - d = startDaemon(particl_settings['datadir'], particl_settings['bindir'], cfg.PARTICLD, ['-noconnect', '-nofindpeers', '-nostaking', '-nodnsseed', '-nolisten']) + + daemons = [] + daemons.append(startDaemon(particl_settings['datadir'], particl_settings['bindir'], cfg.PARTICLD, ['-noconnect', '-nofindpeers', '-nostaking', '-nodnsseed', '-nolisten'])) try: waitForRPC(partRpc) if particl_wallet_mnemonic is None: particl_wallet_mnemonic = partRpc('mnemonic new')['mnemonic'] partRpc('extkeyimportmaster "{}"'.format(particl_wallet_mnemonic)) + + # Initialise wallets + with open(os.path.join(data_dir, 'basicswap.log'), 'a') as fp: + swap_client = BasicSwap(fp, data_dir, settings, chain) + + swap_client.setCoinConnectParams(Coins.PART) + swap_client.setDaemonPID(Coins.PART, daemons[-1].pid) + swap_client.setCoinRunParams(Coins.PART) + swap_client.createCoinInterface(Coins.PART) + + for coin_name in with_coins: + coin_settings = settings['chainclients'][coin_name] + c = swap_client.getCoinIdFromName(coin_name) + if c == Coins.PART: + continue + + swap_client.setCoinConnectParams(c) + + if c == Coins.XMR: + if not coin_settings['manage_wallet_daemon']: + continue + daemons.append(startXmrWalletDaemon(coin_settings['datadir'], coin_settings['bindir'], 'monero-wallet-rpc')) + else: + if not coin_settings['manage_daemon']: + continue + filename = coin_name + 'd' + ('.exe' if os.name == 'nt' else '') + daemons.append(startDaemon(coin_settings['datadir'], coin_settings['bindir'], filename, ['-noconnect', '-nodnsseed', '-nolisten'])) + swap_client.setDaemonPID(c, daemons[-1].pid) + swap_client.setCoinRunParams(c) + swap_client.createCoinInterface(c) + swap_client.waitForDaemonRPC(c) + swap_client.initialiseWallet(c) finally: - logger.info('Terminating {}'.format(d.pid)) - d.terminate() - d.wait(timeout=120) - if d.stdout: - d.stdout.close() - if d.stderr: - d.stderr.close() - if d.stdin: - d.stdin.close() + for d in daemons: + logging.info('Interrupting {}'.format(d.pid)) + d.send_signal(signal.SIGINT) + d.wait(timeout=120) + for fp in (d.stdout, d.stderr, d.stdin): + if fp: + fp.close() logger.info('IMPORTANT - Save your particl wallet recovery phrase:\n{}\n'.format(particl_wallet_mnemonic)) - logger.info('Done.') diff --git a/bin/basicswap_run.py b/bin/basicswap_run.py index 8649103..0956ce3 100644 --- a/bin/basicswap_run.py +++ b/bin/basicswap_run.py @@ -19,7 +19,6 @@ import logging import traceback import subprocess - import basicswap.config as cfg from basicswap import __version__ from basicswap.basicswap import BasicSwap @@ -31,7 +30,6 @@ logger.level = logging.DEBUG if not len(logger.handlers): logger.addHandler(logging.StreamHandler(sys.stdout)) -ALLOW_CORS = False swap_client = None @@ -50,6 +48,29 @@ def startDaemon(node_dir, bin_dir, daemon_bin, opts=[]): return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +def startXmrDaemon(node_dir, bin_dir, daemon_bin, opts=[]): + daemon_bin = os.path.expanduser(os.path.join(bin_dir, daemon_bin)) + + args = [daemon_bin, '--config-file=' + os.path.join(os.path.expanduser(node_dir), 'monerod.conf')] + opts + logging.info('Starting node {} --data-dir={}'.format(daemon_bin, node_dir)) + + return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + +def startXmrWalletDaemon(node_dir, bin_dir, wallet_bin, opts=[]): + daemon_bin = os.path.expanduser(os.path.join(bin_dir, wallet_bin)) + + data_dir = os.path.expanduser(node_dir) + args = [daemon_bin, '--config-file=' + os.path.join(os.path.expanduser(node_dir), 'monero_wallet.conf')] + opts + + args += opts + logging.info('Starting wallet daemon {} --wallet-dir={}'.format(daemon_bin, node_dir)) + + #return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=data_dir) + wallet_stdout = open(os.path.join(data_dir, 'wallet_stdout.log'), 'w') + wallet_stderr = open(os.path.join(data_dir, 'wallet_stderr.log'), 'w') + return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=wallet_stdout, stderr=wallet_stderr, cwd=data_dir) + def runClient(fp, data_dir, chain): global swap_client settings_path = os.path.join(data_dir, cfg.CONFIG_FILENAME) @@ -79,6 +100,20 @@ def runClient(fp, data_dir, chain): try: # Try start daemons for c, v in settings['chainclients'].items(): + if c == 'monero': + if v['manage_daemon'] is True: + logger.info('Starting {} daemon'.format(c.capitalize())) + daemons.append(startXmrDaemon(v['datadir'], v['bindir'], 'monerod')) + pid = daemons[-1].pid + logger.info('Started {} {}'.format('monerod', pid)) + + if v['manage_wallet_daemon'] is True: + logger.info('Starting {} wallet daemon'.format(c.capitalize())) + daemons.append(startXmrWalletDaemon(v['datadir'], v['bindir'], 'monero-wallet-rpc')) + pid = daemons[-1].pid + logger.info('Started {} {}'.format('monero-wallet-rpc', pid)) + + continue if v['manage_daemon'] is True: logger.info('Starting {} daemon'.format(c.capitalize())) @@ -99,7 +134,7 @@ def runClient(fp, data_dir, chain): if 'htmlhost' in settings: swap_client.log.info('Starting server at %s:%d.' % (settings['htmlhost'], settings['htmlport'])) - allow_cors = settings['allowcors'] if 'allowcors' in settings else ALLOW_CORS + allow_cors = settings['allowcors'] if 'allowcors' in settings else cfg.DEFAULT_ALLOW_CORS tS1 = HttpThread(fp, settings['htmlhost'], settings['htmlport'], allow_cors, swap_client) threads.append(tS1) tS1.start() @@ -118,8 +153,7 @@ def runClient(fp, data_dir, chain): closed_pids = [] for d in daemons: - int_pid = d.pid - logging.info('Interrupting {}'.format(int_pid)) + logging.info('Interrupting {}'.format(d.pid)) try: d.send_signal(signal.SIGINT) except Exception as e: @@ -127,13 +161,10 @@ def runClient(fp, data_dir, chain): for d in daemons: try: d.wait(timeout=120) - if d.stdout: - d.stdout.close() - if d.stderr: - d.stderr.close() - if d.stdin: - d.stdin.close() - closed_pids.append(int_pid) + for fp in (d.stdout, d.stderr, d.stdin): + if fp: + fp.close() + closed_pids.append(d.pid) except Exception as ex: logger.error('Error: {}'.format(ex)) diff --git a/tests/basicswap/test_other.py b/tests/basicswap/test_other.py index c4f161f..84b4a12 100644 --- a/tests/basicswap/test_other.py +++ b/tests/basicswap/test_other.py @@ -5,8 +5,8 @@ # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. -import unittest import secrets +import unittest import basicswap.contrib.ed25519_fast as edf import basicswap.ed25519_fast_util as edu diff --git a/tests/basicswap/test_prepare.py b/tests/basicswap/test_prepare.py index 4daa5ae..8cf7d64 100644 --- a/tests/basicswap/test_prepare.py +++ b/tests/basicswap/test_prepare.py @@ -7,12 +7,13 @@ import os import sys -import unittest -from unittest.mock import patch -from io import StringIO -import logging -import shutil import json +import shutil +import logging +import unittest + +from io import StringIO +from unittest.mock import patch import basicswap.config as cfg import bin.basicswap_prepare as prepareSystem diff --git a/tests/basicswap/test_reload.py b/tests/basicswap/test_reload.py index 1e34977..da52b16 100644 --- a/tests/basicswap/test_reload.py +++ b/tests/basicswap/test_reload.py @@ -18,17 +18,17 @@ python tests/basicswap/test_reload.py import os import sys -import time -import unittest -import logging -import shutil import json +import time +import shutil +import logging +import unittest import traceback -import multiprocessing import threading -from unittest.mock import patch -from urllib.request import urlopen +import multiprocessing from urllib import parse +from urllib.request import urlopen +from unittest.mock import patch from basicswap.rpc import ( callrpc_cli, diff --git a/tests/basicswap/test_reload_xmr.py b/tests/basicswap/test_reload_xmr.py new file mode 100644 index 0000000..aeb64f3 --- /dev/null +++ b/tests/basicswap/test_reload_xmr.py @@ -0,0 +1,264 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2020 tecnovert +# Distributed under the MIT software license, see the accompanying +# file LICENSE or http://www.opensource.org/licenses/mit-license.php. + +""" +export TEST_RELOAD_PATH=/tmp/test_basicswap +mkdir -p ${TEST_RELOAD_PATH}/bin/{particl,monero} +cp ~/tmp/particl-0.19.1.2-x86_64-linux-gnu.tar.gz ${TEST_RELOAD_PATH}/bin/particl +cp ~/tmp/monero-0.17.1.5-x86_64-linux-gnu.tar.gz ${TEST_RELOAD_PATH}/bin/monero +export PYTHONPATH=$(pwd) +python tests/basicswap/test_reload_xmr.py + + +""" + +import os +import sys +import json +import time +import shutil +import logging +import unittest +import traceback +import threading +import multiprocessing +from urllib import parse +from urllib.request import urlopen +from unittest.mock import patch + + +from basicswap.rpc import ( + callrpc_cli, +) +from basicswap.util import ( + dumpj +) +from tests.basicswap.mnemonics import mnemonics + +import basicswap.config as cfg +import bin.basicswap_prepare as prepareSystem +import bin.basicswap_run as runSystem + +test_path = os.path.expanduser(os.getenv('TEST_RELOAD_PATH', '~/test_basicswap1')) +PARTICL_PORT_BASE = int(os.getenv('PARTICL_PORT_BASE', '11938')) + +XMR_BASE_P2P_PORT = 17792 +XMR_BASE_RPC_PORT = 21792 +XMR_BASE_ZMQ_PORT = 22792 +XMR_BASE_WALLET_RPC_PORT = 23792 + +stop_test = False + +logger = logging.getLogger() +logger.level = logging.DEBUG +if not len(logger.handlers): + logger.addHandler(logging.StreamHandler(sys.stdout)) + + +def waitForServer(port): + for i in range(20): + try: + time.sleep(1) + summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read()) + break + except Exception: + traceback.print_exc() + + +def waitForNumOffers(port, offers): + for i in range(20): + summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read()) + if summary['num_network_offers'] >= offers: + return + time.sleep(1) + raise ValueError('waitForNumOffers failed') + + +def waitForNumBids(port, bids): + for i in range(20): + summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read()) + if summary['num_recv_bids'] >= bids: + return + time.sleep(1) + raise ValueError('waitForNumBids failed') + + +def waitForNumSwapping(port, bids): + for i in range(20): + summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read()) + if summary['num_swapping'] >= bids: + return + time.sleep(1) + raise ValueError('waitForNumSwapping failed') + + +def updateThread(xmr_addr): + #btc_addr = btcRpc(0, 'getnewaddress mining_addr bech32') + + while not stop_test: + #btcRpc(0, 'generatetoaddress {} {}'.format(1, btc_addr)) + time.sleep(5) + + +class Test(unittest.TestCase): + @classmethod + def setUpClass(cls): + super(Test, cls).setUpClass() + + for i in range(3): + client_path = os.path.join(test_path, 'client{}'.format(i)) + config_path = os.path.join(client_path, cfg.CONFIG_FILENAME) + try: + shutil.rmtree(client_path) + except Exception as ex: + logger.warning('setUpClass %s', str(ex)) + testargs = [ + 'basicswap-prepare', + '-datadir="{}"'.format(client_path), + '-bindir="{}"'.format(os.path.join(test_path, 'bin')), + '-portoffset={}'.format(i), + '-particl_mnemonic="{}"'.format(mnemonics[i]), + '-regtest', '-withoutcoin=litecoin', '-withcoin=monero'] + with patch.object(sys, 'argv', testargs): + prepareSystem.main() + + with open(os.path.join(client_path, 'particl', 'particl.conf'), 'r') as fp: + lines = fp.readlines() + with open(os.path.join(client_path, 'particl', 'particl.conf'), 'w') as fp: + for line in lines: + if not line.startswith('staking'): + fp.write(line) + fp.write('port={}\n'.format(PARTICL_PORT_BASE + i)) + fp.write('bind=127.0.0.1\n') + fp.write('dnsseed=0\n') + fp.write('minstakeinterval=5\n') + for ip in range(3): + if ip != i: + fp.write('connect=localhost:{}\n'.format(PARTICL_PORT_BASE + ip)) + + with open(os.path.join(client_path, 'monero', 'monerod.conf'), 'a') as fp: + fp.write('p2p-bind-ip=127.0.0.1\n') + fp.write('p2p-bind-port={}\n'.format(XMR_BASE_P2P_PORT + i)) + for ip in range(3): + if ip != i: + fp.write('add-exclusive-node=127.0.0.1:{}\n'.format(XMR_BASE_P2P_PORT + ip)) + + assert(os.path.exists(config_path)) + + def run_thread(self, client_id): + client_path = os.path.join(test_path, 'client{}'.format(client_id)) + testargs = ['basicswap-run', '-datadir=' + client_path, '-regtest'] + with patch.object(sys, 'argv', testargs): + runSystem.main() + + def test_reload(self): + global stop_test + update_thread = None + processes = [] + + for i in range(3): + processes.append(multiprocessing.Process(target=self.run_thread, args=(i,))) + processes[-1].start() + + try: + waitForServer(12700) + + wallets = json.loads(urlopen('http://localhost:12701/json/wallets').read()) + print('[rm] wallets', dumpj(wallets)) + + xmr_addr1 = wallets['6']['deposit_address'] + num_blocks = 500 + + raise ValueError('TODO') + ''' + + btc_addr = btcRpc(1, 'getnewaddress mining_addr bech32') + logging.info('Mining %d Bitcoin blocks to %s', num_blocks, btc_addr) + btcRpc(1, 'generatetoaddress {} {}'.format(num_blocks, btc_addr)) + + for i in range(20): + blocks = btcRpc(0, 'getblockchaininfo')['blocks'] + if blocks >= 500: + break + assert(blocks >= 500) + ''' + + data = parse.urlencode({ + 'addr_from': '-1', + 'coin_from': '1', + 'coin_to': '6', + 'amt_from': '1', + 'amt_to': '1', + 'lockhrs': '24'}).encode() + + offer_id = json.loads(urlopen('http://localhost:12700/json/offers/new', data=data).read()) + summary = json.loads(urlopen('http://localhost:12700/json').read()) + assert(summary['num_sent_offers'] == 1) + + + logger.info('Waiting for offer:') + waitForNumOffers(12701, 1) + + offers = json.loads(urlopen('http://localhost:12701/json/offers').read()) + offer = offers[0] + + data = parse.urlencode({ + 'offer_id': offer['offer_id'], + 'amount_from': offer['amount_from']}).encode() + + bid_id = json.loads(urlopen('http://localhost:12701/json/bids/new', data=data).read()) + + waitForNumBids(12700, 1) + + bids = json.loads(urlopen('http://localhost:12700/json/bids').read()) + bid = bids[0] + + data = parse.urlencode({ + 'accept': True + }).encode() + rv = json.loads(urlopen('http://localhost:12700/json/bids/{}'.format(bid['bid_id']), data=data).read()) + assert(rv['bid_state'] == 'Accepted') + + waitForNumSwapping(12701, 1) + + logger.info('Restarting client:') + c1 = processes[1] + c1.terminate() + c1.join() + processes[1] = multiprocessing.Process(target=self.run_thread, args=(1,)) + processes[1].start() + + waitForServer(12701) + rv = json.loads(urlopen('http://localhost:12701/json').read()) + assert(rv['num_swapping'] == 1) + + update_thread = threading.Thread(target=updateThread, args=(xmr_addr,)) + update_thread.start() + + logger.info('Completing swap:') + for i in range(240): + time.sleep(5) + + rv = json.loads(urlopen('http://localhost:12700/json/bids/{}'.format(bid['bid_id'])).read()) + print(rv) + if rv['bid_state'] == 'Completed': + break + assert(rv['bid_state'] == 'Completed') + except Exception: + traceback.print_exc() + + stop_test = True + if update_thread: + update_thread.join() + for p in processes: + p.terminate() + for p in processes: + p.join() + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/basicswap/test_wallet_init.py b/tests/basicswap/test_wallet_init.py new file mode 100644 index 0000000..b8246e4 --- /dev/null +++ b/tests/basicswap/test_wallet_init.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2020 tecnovert +# Distributed under the MIT software license, see the accompanying +# file LICENSE or http://www.opensource.org/licenses/mit-license.php. + +""" +export TEST_PATH=/tmp/test_basicswap_wallet_init +mkdir -p ${TEST_PATH}/bin/{particl,monero,bitcoin} +cp ~/tmp/particl-0.19.1.2-x86_64-linux-gnu.tar.gz ${TEST_PATH}/bin/particl +cp ~/tmp/monero-0.17.1.5-x86_64-linux-gnu.tar.gz ${TEST_PATH}/bin/monero +cp ~/tmp/bitcoin-0.20.1-x86_64-linux-gnu.tar.gz ${TEST_PATH}/bin/bitcoin +export PYTHONPATH=$(pwd) +python tests/basicswap/test_wallet_init.py + + +""" + +import os +import sys +import json +import time +import shutil +import logging +import unittest +import traceback +import threading +import multiprocessing +from urllib import parse +from urllib.request import urlopen +from unittest.mock import patch + + +from basicswap.rpc import ( + callrpc_cli, +) +from basicswap.util import ( + dumpj +) +from tests.basicswap.mnemonics import mnemonics + +import basicswap.config as cfg +import bin.basicswap_prepare as prepareSystem +import bin.basicswap_run as runSystem + +test_path = os.path.expanduser(os.getenv('TEST_PATH', '~/test_basicswap1')) +PARTICL_PORT_BASE = int(os.getenv('PARTICL_PORT_BASE', '11938')) +BITCOIN_PORT_BASE = int(os.getenv('BITCOIN_PORT_BASE', '10938')) +XMR_BASE_P2P_PORT = 17792 +XMR_BASE_RPC_PORT = 21792 +XMR_BASE_ZMQ_PORT = 22792 +XMR_BASE_WALLET_RPC_PORT = 23792 + +stop_test = False + +logger = logging.getLogger() +logger.level = logging.DEBUG +if not len(logger.handlers): + logger.addHandler(logging.StreamHandler(sys.stdout)) + + +def waitForServer(port): + for i in range(20): + try: + time.sleep(1) + summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read()) + break + except Exception: + traceback.print_exc() + + +def waitForNumOffers(port, offers): + for i in range(20): + summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read()) + if summary['num_network_offers'] >= offers: + return + time.sleep(1) + raise ValueError('waitForNumOffers failed') + + +def waitForNumBids(port, bids): + for i in range(20): + summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read()) + if summary['num_recv_bids'] >= bids: + return + time.sleep(1) + raise ValueError('waitForNumBids failed') + + +def waitForNumSwapping(port, bids): + for i in range(20): + summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read()) + if summary['num_swapping'] >= bids: + return + time.sleep(1) + raise ValueError('waitForNumSwapping failed') + + +class Test(unittest.TestCase): + @classmethod + def setUpClass(cls): + super(Test, cls).setUpClass() + + for i in range(2): + client_path = os.path.join(test_path, 'client{}'.format(i)) + config_path = os.path.join(client_path, cfg.CONFIG_FILENAME) + try: + shutil.rmtree(client_path) + except Exception as ex: + logger.warning('setUpClass %s', str(ex)) + testargs = [ + 'basicswap-prepare', + '-datadir="{}"'.format(client_path), + '-bindir="{}"'.format(os.path.join(test_path, 'bin')), + '-portoffset={}'.format(i), + '-particl_mnemonic="{}"'.format(mnemonics[0]), + '-regtest', '-withoutcoin=litecoin', '-withcoin=monero,bitcoin'] + with patch.object(sys, 'argv', testargs): + prepareSystem.main() + + with open(os.path.join(client_path, 'particl', 'particl.conf'), 'r') as fp: + lines = fp.readlines() + with open(os.path.join(client_path, 'particl', 'particl.conf'), 'w') as fp: + for line in lines: + if not line.startswith('staking'): + fp.write(line) + fp.write('port={}\n'.format(PARTICL_PORT_BASE + i)) + fp.write('bind=127.0.0.1\n') + fp.write('dnsseed=0\n') + fp.write('minstakeinterval=5\n') + for ip in range(3): + if ip != i: + fp.write('connect=localhost:{}\n'.format(PARTICL_PORT_BASE + ip)) + + # Pruned nodes don't provide blocks + with open(os.path.join(client_path, 'bitcoin', 'bitcoin.conf'), 'r') as fp: + lines = fp.readlines() + with open(os.path.join(client_path, 'bitcoin', 'bitcoin.conf'), 'w') as fp: + for line in lines: + if not line.startswith('prune'): + fp.write(line) + fp.write('port={}\n'.format(BITCOIN_PORT_BASE + i)) + fp.write('discover=0\n') + fp.write('dnsseed=0\n') + fp.write('listenonion=0\n') + fp.write('upnp=0\n') + fp.write('bind=127.0.0.1\n') + for ip in range(3): + if ip != i: + fp.write('connect=localhost:{}\n'.format(BITCOIN_PORT_BASE + ip)) + + with open(os.path.join(client_path, 'monero', 'monerod.conf'), 'a') as fp: + fp.write('p2p-bind-ip=127.0.0.1\n') + fp.write('p2p-bind-port={}\n'.format(XMR_BASE_P2P_PORT + i)) + for ip in range(3): + if ip != i: + fp.write('add-exclusive-node=127.0.0.1:{}\n'.format(XMR_BASE_P2P_PORT + ip)) + + assert(os.path.exists(config_path)) + + def run_thread(self, client_id): + client_path = os.path.join(test_path, 'client{}'.format(client_id)) + testargs = ['basicswap-run', '-datadir=' + client_path, '-regtest'] + with patch.object(sys, 'argv', testargs): + runSystem.main() + + def test_wallet(self): + global stop_test + update_thread = None + processes = [] + + time.sleep(5) + for i in range(2): + processes.append(multiprocessing.Process(target=self.run_thread, args=(i,))) + processes[-1].start() + + try: + waitForServer(12700) + + wallets = json.loads(urlopen('http://localhost:12700/json/wallets').read()) + print('[rm] wallets', dumpj(wallets)) + + waitForServer(12701) + wallets = json.loads(urlopen('http://localhost:12701/json/wallets').read()) + print('[rm] wallets', dumpj(wallets)) + + raise ValueError('TODO') + except Exception: + traceback.print_exc() + + stop_test = True + if update_thread: + update_thread.join() + for p in processes: + p.terminate() + for p in processes: + p.join() + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py index 48fcc3b..de99b12 100644 --- a/tests/basicswap/test_xmr.py +++ b/tests/basicswap/test_xmr.py @@ -55,7 +55,7 @@ from tests.basicswap.common import ( TEST_HTTP_PORT, ) from basicswap.contrib.rpcauth import generate_salt, password_to_hmac -from bin.basicswap_run import startDaemon +from bin.basicswap_run import startDaemon, startXmrDaemon logger = logging.getLogger() @@ -479,12 +479,9 @@ class Test(unittest.TestCase): for d in cls.xmr_daemons: try: d.wait(timeout=20) - if d.stdout: - d.stdout.close() - if d.stderr: - d.stderr.close() - if d.stdin: - d.stdin.close() + for fp in (d.stdout, d.stderr, d.stdin): + if fp: + fp.close() except Exception as e: logging.info('Closing %d, error %s', d.pid, str(e)) @@ -497,12 +494,9 @@ class Test(unittest.TestCase): for d in cls.part_daemons + cls.btc_daemons: try: d.wait(timeout=20) - if d.stdout: - d.stdout.close() - if d.stderr: - d.stderr.close() - if d.stdin: - d.stdin.close() + for fp in (d.stdout, d.stderr, d.stdin): + if fp: + fp.close() except Exception as e: logging.info('Closing %d, error %s', d.pid, str(e))