From 64caceebfed40ce6735a9fc2146af1fdc22fe530 Mon Sep 17 00:00:00 2001 From: tecnovert Date: Sat, 16 Jan 2021 10:21:59 +0200 Subject: [PATCH] Persistent test. --- .cirrus.yml | 3 - .travis.yml | 3 - bin/basicswap_prepare.py | 20 +- tests/basicswap/common.py | 2 +- tests/basicswap/common_xmr.py | 3 + tests/basicswap/extended/test_wallet_init.py | 7 +- .../basicswap/extended/test_xmr_persistent.py | 296 ++++++++++++++++++ tests/basicswap/test_reload.py | 7 +- 8 files changed, 324 insertions(+), 17 deletions(-) create mode 100644 tests/basicswap/extended/test_xmr_persistent.py diff --git a/.cirrus.yml b/.cirrus.yml index a46228f..f369b8e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -12,7 +12,6 @@ lint_task: test_task: environment: - - TEST_PREPARE_PATH: $HOME/test_basicswap-prepare - TEST_RELOAD_PATH: $HOME/test_basicswap1 - TEST_DIR: $HOME/test_basicswap2 - BIN_DIR: /tmp/cached_bin @@ -43,6 +42,4 @@ test_task: - cp -r ${BIN_DIR} "${DATADIRS}/bin" - mkdir -p "${TEST_RELOAD_PATH}/bin" - cp -r ${BIN_DIR} "${TEST_RELOAD_PATH}/bin" - - mkdir -p "${TEST_PREPARE_PATH}/bin" - - cp -r ${BIN_DIR} "${TEST_PREPARE_PATH}/bin" - tox diff --git a/.travis.yml b/.travis.yml index d414c4e..18f065d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ stages: - test env: global: - - TEST_PREPARE_PATH=~/test_basicswap-prepare - TEST_DIR=${HOME}/test_basicswap2 - TEST_RELOAD_PATH=~/test_basicswap1 - BIN_DIR=~/cached_bin @@ -36,8 +35,6 @@ script: - cp -r ${BIN_DIR} "${DATADIRS}/bin" - mkdir -p "${TEST_RELOAD_PATH}/bin" - cp -r ${BIN_DIR} "${TEST_RELOAD_PATH}/bin" - - mkdir -p "${TEST_PREPARE_PATH}/bin" - - cp -r ${BIN_DIR} "${TEST_PREPARE_PATH}/bin" - tox after_success: - echo "End test" diff --git a/bin/basicswap_prepare.py b/bin/basicswap_prepare.py index 194dc30..b02f65d 100755 --- a/bin/basicswap_prepare.py +++ b/bin/basicswap_prepare.py @@ -64,12 +64,20 @@ XMR_SITE_COMMIT = 'd27c1eee9fe0e8daa011d07baae8b67dd2b62a04' # Lock hashes.txt DEFAULT_XMR_RESTORE_HEIGHT = 2245107 +UI_HTML_PORT = int(os.getenv('BASE_XMR_RPC_PORT', 12700)) +PART_ZMQ_PORT = int(os.getenv('PART_ZMQ_PORT', 20792)) PART_RPC_HOST = os.getenv('PART_RPC_HOST', '127.0.0.1') LTC_RPC_HOST = os.getenv('LTC_RPC_HOST', '127.0.0.1') BTC_RPC_HOST = os.getenv('BTC_RPC_HOST', '127.0.0.1') NMC_RPC_HOST = os.getenv('NMC_RPC_HOST', '127.0.0.1') +PART_RPC_PORT = int(os.getenv('PART_RPC_PORT', 19792)) +LTC_RPC_PORT = int(os.getenv('LTC_RPC_PORT', 19795)) +BTC_RPC_PORT = int(os.getenv('BTC_RPC_PORT', 19796)) +NMC_RPC_PORT = int(os.getenv('NMC_RPC_PORT', 19798)) + + extract_core_overwrite = True @@ -537,7 +545,7 @@ def main(): 'connection_type': 'rpc', 'manage_daemon': True if ('particl' in with_coins and PART_RPC_HOST == '127.0.0.1') else False, 'rpchost': PART_RPC_HOST, - 'rpcport': 19792 + port_offset, + 'rpcport': PART_RPC_PORT + port_offset, 'datadir': os.getenv('PART_DATA_DIR', os.path.join(data_dir, 'particl')), 'bindir': os.path.join(bin_dir, 'particl'), 'blocks_confirmed': 2, @@ -550,7 +558,7 @@ def main(): 'connection_type': 'rpc' if 'litecoin' in with_coins else 'none', 'manage_daemon': True if ('litecoin' in with_coins and LTC_RPC_HOST == '127.0.0.1') else False, 'rpchost': LTC_RPC_HOST, - 'rpcport': 19795 + port_offset, + 'rpcport': LTC_RPC_PORT + port_offset, 'datadir': os.getenv('LTC_DATA_DIR', os.path.join(data_dir, 'litecoin')), 'bindir': os.path.join(bin_dir, 'litecoin'), 'use_segwit': True, @@ -563,7 +571,7 @@ def main(): 'connection_type': 'rpc' if 'bitcoin' in with_coins else 'none', 'manage_daemon': True if ('bitcoin' in with_coins and BTC_RPC_HOST == '127.0.0.1') else False, 'rpchost': BTC_RPC_HOST, - 'rpcport': 19796 + port_offset, + 'rpcport': BTC_RPC_PORT + port_offset, 'datadir': os.getenv('BTC_DATA_DIR', os.path.join(data_dir, 'bitcoin')), 'bindir': os.path.join(bin_dir, 'bitcoin'), 'use_segwit': True, @@ -576,7 +584,7 @@ def main(): 'connection_type': 'rpc' if 'namecoin' in with_coins else 'none', 'manage_daemon': True if ('namecoin' in with_coins and NMC_RPC_HOST == '127.0.0.1') else False, 'rpchost': NMC_RPC_HOST, - 'rpcport': 19798 + port_offset, + 'rpcport': NMC_RPC_PORT + port_offset, 'datadir': os.getenv('NMC_DATA_DIR', os.path.join(data_dir, 'namecoin')), 'bindir': os.path.join(bin_dir, 'namecoin'), 'use_segwit': False, @@ -669,9 +677,9 @@ def main(): settings = { 'debug': True, 'zmqhost': 'tcp://127.0.0.1', - 'zmqport': 20792 + port_offset, + 'zmqport': PART_ZMQ_PORT + port_offset, 'htmlhost': htmlhost, - 'htmlport': 12700 + port_offset, + 'htmlport': UI_HTML_PORT + port_offset, 'network_key': '7sW2UEcHXvuqEjkpE5mD584zRaQYs6WXYohue4jLFZPTvMSxwvgs', 'network_pubkey': '035758c4a22d7dd59165db02a56156e790224361eb3191f02197addcb3bde903d2', 'chainclients': withchainclients, diff --git a/tests/basicswap/common.py b/tests/basicswap/common.py index 6e5d2b1..cbe643b 100644 --- a/tests/basicswap/common.py +++ b/tests/basicswap/common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright (c) 2020 tecnovert +# Copyright (c) 2020-2021 tecnovert # Distributed under the MIT software license, see the accompanying # file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/basicswap/common_xmr.py b/tests/basicswap/common_xmr.py index 38d6136..7853531 100644 --- a/tests/basicswap/common_xmr.py +++ b/tests/basicswap/common_xmr.py @@ -95,6 +95,9 @@ class XmrTestBase(unittest.TestCase): fp.write('port={}\n'.format(PARTICL_PORT_BASE + i)) fp.write('bind=127.0.0.1\n') fp.write('dnsseed=0\n') + fp.write('discover=0\n') + fp.write('listenonion=0\n') + fp.write('upnp=0\n') fp.write('minstakeinterval=5\n') for ip in range(3): if ip != i: diff --git a/tests/basicswap/extended/test_wallet_init.py b/tests/basicswap/extended/test_wallet_init.py index 70aa00b..d405815 100644 --- a/tests/basicswap/extended/test_wallet_init.py +++ b/tests/basicswap/extended/test_wallet_init.py @@ -95,6 +95,9 @@ class Test(unittest.TestCase): fp.write('port={}\n'.format(PARTICL_PORT_BASE + i)) fp.write('bind=127.0.0.1\n') fp.write('dnsseed=0\n') + fp.write('discover=0\n') + fp.write('listenonion=0\n') + fp.write('upnp=0\n') fp.write('minstakeinterval=5\n') for ip in range(3): if ip != i: @@ -108,11 +111,11 @@ class Test(unittest.TestCase): if not line.startswith('prune'): fp.write(line) fp.write('port={}\n'.format(BITCOIN_PORT_BASE + i)) - fp.write('discover=0\n') + fp.write('bind=127.0.0.1\n') fp.write('dnsseed=0\n') + fp.write('discover=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=127.0.0.1:{}\n'.format(BITCOIN_PORT_BASE + ip)) diff --git a/tests/basicswap/extended/test_xmr_persistent.py b/tests/basicswap/extended/test_xmr_persistent.py new file mode 100644 index 0000000..a2beb7f --- /dev/null +++ b/tests/basicswap/extended/test_xmr_persistent.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2021 tecnovert +# Distributed under the MIT software license, see the accompanying +# file LICENSE or http://www.opensource.org/licenses/mit-license.php. + +""" +export RESET_TEST=true +export TEST_PATH=/tmp/test_persistent +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/bitcoin-0.20.1-x86_64-linux-gnu.tar.gz ${TEST_PATH}/bin/bitcoin +cp ~/tmp/monero-linux-x64-v0.17.1.9.tar.bz2 ${TEST_PATH}/bin/monero/monero-0.17.1.9-x86_64-linux-gnu.tar.bz2 +export PYTHONPATH=$(pwd) +python tests/basicswap/extended/test_xmr_persistent.py + + +""" + +import os +import sys +import json +import time +import random +import shutil +import signal +import logging +import unittest +import threading +import multiprocessing +from urllib.request import urlopen +from unittest.mock import patch + +from basicswap.rpc_xmr import ( + callrpc_xmr_na, +) +from basicswap.rpc import ( + callrpc, +) +from tests.basicswap.mnemonics import mnemonics +from tests.basicswap.common import ( + waitForServer, +) +from basicswap.contrib.rpcauth import generate_salt, password_to_hmac + +import basicswap.config as cfg +import bin.basicswap_prepare as prepareSystem +import bin.basicswap_run as runSystem + + +def make_boolean(s): + return s.lower() in ['1', 'true'] + + +test_path = os.path.expanduser(os.getenv('TEST_PATH', '/tmp/test_persistent')) +PARTICL_PORT_BASE = int(os.getenv('PARTICL_PORT_BASE', '11938')) +BITCOIN_PORT_BASE = int(os.getenv('BITCOIN_PORT_BASE', '10938')) +RESET_TEST = make_boolean(os.getenv('RESET_TEST', 'false')) + +XMR_BASE_P2P_PORT = 17792 +XMR_BASE_RPC_PORT = 29798 +XMR_BASE_WALLET_RPC_PORT = 29998 + +PORT_OFS = 1 +UI_PORT = 12700 + PORT_OFS + +BASE_PART_RPC_PORT = 19792 +BASE_BTC_RPC_PORT = 19796 + +logger = logging.getLogger() +logger.level = logging.DEBUG +if not len(logger.handlers): + logger.addHandler(logging.StreamHandler(sys.stdout)) + + +def callpartrpc(node_id, method, params=[], wallet=None, base_rpc_port=BASE_PART_RPC_PORT+PORT_OFS): + auth = 'test_part_{0}:test_part_pwd_{0}'.format(node_id) + return callrpc(base_rpc_port + node_id, auth, method, params, wallet) + +def callbtcrpc(node_id, method, params=[], wallet=None, base_rpc_port=BASE_BTC_RPC_PORT+PORT_OFS): + auth = 'test_btc_{0}:test_btc_pwd_{0}'.format(node_id) + return callrpc(base_rpc_port + node_id, auth, method, params, wallet) + + +def updateThread(cls): + while not cls.delay_event.is_set(): + try: + if cls.btc_addr is not None: + callbtcrpc(0, 'generatetoaddress', [1, cls.btc_addr]) + if cls.xmr_addr is not None: + callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'generateblocks', {'wallet_address': cls.xmr_addr, 'amount_of_blocks': 1}) + except Exception as e: + print('updateThread error', str(e)) + cls.delay_event.wait(random.randrange(1, 4)) + + +class Test(unittest.TestCase): + @classmethod + def setUpClass(cls): + super(Test, cls).setUpClass() + + cls.delay_event = threading.Event() + cls.update_thread = None + cls.processes = [] + cls.btc_addr = None + cls.xmr_addr = None + + random.seed(time.time()) + + 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) + if RESET_TEST: + try: + logging.info('Removing dir %s', client_path) + shutil.rmtree(client_path) + except Exception as ex: + logging.warning('setUpClass %s', str(ex)) + + if not os.path.exists(config_path): + + os.environ['PART_RPC_PORT'] = str(BASE_PART_RPC_PORT) + os.environ['BTC_RPC_PORT'] = str(BASE_BTC_RPC_PORT) + + testargs = [ + 'basicswap-prepare', + '-datadir="{}"'.format(client_path), + '-bindir="{}"'.format(os.path.join(test_path, 'bin')), + '-portoffset={}'.format(i + PORT_OFS), + '-particl_mnemonic="{}"'.format(mnemonics[i]), + '-regtest', + '-withcoins=monero,bitcoin', + '-noextractover', + '-xmrrestoreheight=0'] + 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 + PORT_OFS)) + fp.write('bind=127.0.0.1\n') + fp.write('dnsseed=0\n') + fp.write('discover=0\n') + fp.write('listenonion=0\n') + fp.write('upnp=0\n') + fp.write('minstakeinterval=5\n') + salt = generate_salt(16) + fp.write('rpcauth={}:{}${}\n'.format('test_part_' + str(i), salt, password_to_hmac(salt, 'test_part_pwd_' + str(i)))) + for ip in range(3): + if ip != i: + fp.write('connect=127.0.0.1:{}\n'.format(PARTICL_PORT_BASE + ip + PORT_OFS)) + + # 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 + PORT_OFS)) + fp.write('bind=127.0.0.1\n') + fp.write('dnsseed=0\n') + fp.write('discover=0\n') + fp.write('listenonion=0\n') + fp.write('upnp=0\n') + salt = generate_salt(16) + fp.write('rpcauth={}:{}${}\n'.format('test_btc_' + str(i), salt, password_to_hmac(salt, 'test_btc_pwd_' + str(i)))) + for ip in range(3): + if ip != i: + fp.write('connect=127.0.0.1:{}\n'.format(BITCOIN_PORT_BASE + ip + PORT_OFS)) + + 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 + PORT_OFS)) + for ip in range(3): + if ip != i: + fp.write('add-exclusive-node=127.0.0.1:{}\n'.format(XMR_BASE_P2P_PORT + ip + PORT_OFS)) + + with open(config_path) as fs: + settings = json.load(fs) + + settings['min_delay_event'] = 1 + settings['max_delay_event'] = 4 + settings['min_delay_retry'] = 10 + settings['max_delay_retry'] = 20 + + settings['check_progress_seconds'] = 5 + settings['check_watched_seconds'] = 5 + settings['check_expired_seconds'] = 60 + settings['check_events_seconds'] = 5 + settings['check_xmr_swaps_seconds'] = 5 + + settings['chainclients']['particl']['rpcuser'] = 'test_part_' + str(i) + settings['chainclients']['particl']['rpcpassword'] = 'test_part_pwd_' + str(i) + + settings['chainclients']['bitcoin']['rpcuser'] = 'test_btc_' + str(i) + settings['chainclients']['bitcoin']['rpcpassword'] = 'test_btc_pwd_' + str(i) + + with open(config_path, 'w') as fp: + json.dump(settings, fp, indent=4) + + signal.signal(signal.SIGINT, lambda signal, frame: cls.signal_handler(cls, signal, frame)) + + def signal_handler(self, sig, frame): + logging.info('signal {} detected.'.format(sig)) + self.delay_event.set() + + 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 start_processes(self): + self.delay_event.clear() + + for i in range(3): + self.processes.append(multiprocessing.Process(target=self.run_thread, args=(i,))) + self.processes[-1].start() + + waitForServer(self.delay_event, UI_PORT + 0) + waitForServer(self.delay_event, UI_PORT + 1) + + wallets = json.loads(urlopen('http://127.0.0.1:{}/json/wallets'.format(UI_PORT + 1)).read()) + + xmr_addr1 = wallets['6']['deposit_address'] + num_blocks = 100 + + if callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'get_block_count')['count'] < num_blocks: + logging.info('Mining {} Monero blocks to {}.'.format(num_blocks, xmr_addr1)) + callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'generateblocks', {'wallet_address': xmr_addr1, 'amount_of_blocks': num_blocks}) + logging.info('XMR blocks: %d', callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'get_block_count')['count']) + + + self.btc_addr = callbtcrpc(0, 'getnewaddress', ['mining_addr', 'bech32']) + + + rv = callbtcrpc(0, 'getblockchaininfo') + print('rv', rv) + + num_blocks = 500 # Mine enough to activate segwit + if callbtcrpc(0, 'getblockchaininfo')['blocks'] < num_blocks: + logging.info('Mining %d Bitcoin blocks to %s', num_blocks, self.btc_addr) + callbtcrpc(0, 'generatetoaddress', [num_blocks, self.btc_addr]) + logging.info('BTC blocks: %d', callbtcrpc(0, 'getblockchaininfo')['blocks']) + + self.update_thread = threading.Thread(target=updateThread, args=(self,)) + self.update_thread.start() + + # Wait for height, or sequencelock is thrown off by genesis blocktime + num_blocks = 3 + logging.info('Waiting for Particl chain height %d', num_blocks) + for i in range(60): + if self.delay_event.is_set(): + raise ValueError('Test stopped.') + particl_blocks = callpartrpc(0, 'getblockchaininfo')['blocks'] + print('particl_blocks', particl_blocks) + if particl_blocks >= num_blocks: + break + self.delay_event.wait(1) + + logging.info('PART blocks: %d', callpartrpc(0, 'getblockchaininfo')['blocks']) + assert(particl_blocks >= num_blocks) + + @classmethod + def tearDownClass(cls): + logging.info('Stopping test') + cls.delay_event.set() + if cls.update_thread: + cls.update_thread.join() + for p in cls.processes: + p.terminate() + for p in cls.processes: + p.join() + cls.update_thread = None + cls.processes = [] + + def test_persistent(self): + + self.start_processes() + + waitForServer(self.delay_event, UI_PORT + 0) + waitForServer(self.delay_event, UI_PORT + 1) + + while not self.delay_event.is_set(): + logging.info('Looping indefinitly, ctrl+c to exit.') + self.delay_event.wait(10) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/basicswap/test_reload.py b/tests/basicswap/test_reload.py index 11c5139..021d715 100644 --- a/tests/basicswap/test_reload.py +++ b/tests/basicswap/test_reload.py @@ -99,6 +99,9 @@ class Test(unittest.TestCase): fp.write('port={}\n'.format(PARTICL_PORT_BASE + i)) fp.write('bind=127.0.0.1\n') fp.write('dnsseed=0\n') + fp.write('discover=0\n') + fp.write('listenonion=0\n') + fp.write('upnp=0\n') fp.write('minstakeinterval=5\n') for ip in range(3): if ip != i: @@ -112,11 +115,11 @@ class Test(unittest.TestCase): if not line.startswith('prune'): fp.write(line) fp.write('port={}\n'.format(BITCOIN_PORT_BASE + i)) - fp.write('discover=0\n') + fp.write('bind=127.0.0.1\n') fp.write('dnsseed=0\n') + fp.write('discover=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=127.0.0.1:{}\n'.format(BITCOIN_PORT_BASE + ip))