diff --git a/basicswap/base.py b/basicswap/base.py index 2bb070d..8dc6009 100644 --- a/basicswap/base.py +++ b/basicswap/base.py @@ -37,6 +37,7 @@ class BaseApp: self.coin_clients = {} self.mxDB = threading.RLock() self.debug = self.settings.get('debug', False) + self._network = None self.prepareLogging() self.log.info('Network: {}'.format(self.chain)) diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 5cc4f49..cfd5249 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -89,6 +89,7 @@ from .explorers import ( ExplorerChainz, ) import basicswap.config as cfg +import basicswap.network as bsn import basicswap.protocols.atomic_swap_1 as atomic_swap_1 @@ -507,6 +508,12 @@ class BasicSwap(BaseApp): random.seed(secrets.randbits(128)) + def finalise(self): + if self._network: + self._network.stopNetwork() + self._network = None + self.log.info('Finalise') + def setCoinConnectParams(self, coin): # Set anything that does not require the daemon to be running chain_client_settings = self.getChainClientSettings(coin) @@ -642,6 +649,10 @@ class BasicSwap(BaseApp): self.coin_clients[c]['have_spent_index'] = ci.haveSpentIndex() # Sanity checks + rv = self.callcoinrpc(c, 'extkey') + if 'result' in rv and 'No keys to list.' in rv['result']: + raise ValueError('No keys loaded.') + if self.callcoinrpc(c, 'getstakinginfo')['enabled'] is not False: self.log.warning('%s staking is not disabled.', ci.coin_name()) elif c == Coins.XMR: @@ -649,6 +660,11 @@ class BasicSwap(BaseApp): self.checkWalletSeed(c) + if 'p2p_host' in self.settings: + network_key = self.getNetworkKey(1) + self._network = bsn.Network(self.settings['p2p_host'], self.settings['p2p_port'], network_key) + self._network.startNetwork() + self.initialise() def stopDaemon(self, coin): @@ -1098,7 +1114,6 @@ class BasicSwap(BaseApp): 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) @@ -1110,7 +1125,6 @@ class BasicSwap(BaseApp): 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) @@ -1124,6 +1138,14 @@ class BasicSwap(BaseApp): return self.grindForEd25519Key(coin_to, evkey, key_path_base) + def getNetworkKey(self, key_num): + evkey = self.callcoinrpc(Coins.PART, 'extkey', ['account', 'default', 'true'])['evkey'] + + key_path = '44445556h/1h/{}'.format(int(key_num)) + + extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path])['key_info']['result'] + return decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey']) + def getContractPubkey(self, date, contract_count): account = self.callcoinrpc(Coins.PART, 'extkey', ['account']) diff --git a/basicswap/js_server.py b/basicswap/js_server.py index 34bc910..d699d81 100644 --- a/basicswap/js_server.py +++ b/basicswap/js_server.py @@ -102,7 +102,11 @@ def js_bids(self, url_split, post_string): offer_id = bytes.fromhex(post_data[b'offer_id'][0].decode('utf-8')) assert(len(offer_id) == 28) - amount_from = inputAmount(post_data[b'amount_from'][0].decode('utf-8')) + offer = swap_client.getOffer(offer_id) + assert(offer), 'Offer not found.' + + ci_from = swap_client.ci(offer.coin_from) + amount_from = inputAmount(post_data[b'amount_from'][0].decode('utf-8'), ci_from) addr_from = None if b'addr_from' in post_data: @@ -110,8 +114,7 @@ def js_bids(self, url_split, post_string): if addr_from == '-1': addr_from = None - offer = swap_client.getOffer(offer_id) - if offer and offer.swap_type == SwapTypes.XMR_SWAP: + if offer.swap_type == SwapTypes.XMR_SWAP: bid_id = swap_client.postXmrBid(offer_id, amount_from, addr_send_from=addr_from).hex() else: bid_id = swap_client.postBid(offer_id, amount_from, addr_send_from=addr_from).hex() diff --git a/basicswap/network.py b/basicswap/network.py index 87701ce..00f5fce 100644 --- a/basicswap/network.py +++ b/basicswap/network.py @@ -12,10 +12,17 @@ TODO: import select import socket import logging +import threading + + +class NetMessage: + def __init__(self): + self._msg_type class Peer: - pass + def __init__(self, address): + self._address = address class Network: @@ -28,14 +35,24 @@ class Network: self._max_connections = 10 self._running = True + self._network_thread = None + self._mx = threading.Lock() + + def startNetwork(self): + pass + + def stopNetwork(self): + pass + def listen(self): self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind((self._p2p_host, self._p2p_port)) self._socket.listen(self._max_connections) + timeout = 1.0 while self._running: - readable, writable, errored = select.select([self._socket], [], []) + readable, writable, errored = select.select([self._socket], [], [], timeout) for s in readable: client_socket, address = self._socket.accept() logging.info('Connection from %s', address) diff --git a/bin/basicswap_run.py b/bin/basicswap_run.py index 125ef1a..e9a2726 100755 --- a/bin/basicswap_run.py +++ b/bin/basicswap_run.py @@ -142,7 +142,8 @@ def runClient(fp, data_dir, chain): except Exception as ex: traceback.print_exc() - swap_client.log.info('Stopping threads.') + swap_client.finalise() + swap_client.log.info('Stopping HTTP threads.') for t in threads: t.stop() t.join() diff --git a/tests/basicswap/common.py b/tests/basicswap/common.py index eb40515..891e2f4 100644 --- a/tests/basicswap/common.py +++ b/tests/basicswap/common.py @@ -6,11 +6,72 @@ # file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php. import os +import json +import signal +import logging +from urllib.request import urlopen + +from basicswap.rpc import callrpc +from basicswap.contrib.rpcauth import generate_salt, password_to_hmac TEST_HTTP_HOST = os.getenv('TEST_HTTP_HOST', '127.0.0.1') # Set to 0.0.0.0 when used in docker TEST_HTTP_PORT = 1800 +BASE_P2P_PORT = 12792 + +BASE_PORT = 14792 +BASE_RPC_PORT = 19792 +BASE_ZMQ_PORT = 20792 + +BTC_BASE_PORT = 31792 +BTC_BASE_RPC_PORT = 32792 +BTC_BASE_ZMQ_PORT = 33792 + +PREFIX_SECRET_KEY_REGTEST = 0x2e + + +def prepareDataDir(datadir, node_id, conf_file, dir_prefix, base_p2p_port=BASE_PORT, base_rpc_port=BASE_RPC_PORT, num_nodes=3): + node_dir = os.path.join(datadir, dir_prefix + str(node_id)) + if not os.path.exists(node_dir): + os.makedirs(node_dir) + cfg_file_path = os.path.join(node_dir, conf_file) + if os.path.exists(cfg_file_path): + return + with open(cfg_file_path, 'w+') as fp: + fp.write('regtest=1\n') + fp.write('[regtest]\n') + fp.write('port=' + str(base_p2p_port + node_id) + '\n') + fp.write('rpcport=' + str(base_rpc_port + node_id) + '\n') + + salt = generate_salt(16) + fp.write('rpcauth={}:{}${}\n'.format('test' + str(node_id), salt, password_to_hmac(salt, 'test_pass' + str(node_id)))) + + fp.write('daemon=0\n') + fp.write('printtoconsole=0\n') + fp.write('server=1\n') + fp.write('discover=0\n') + fp.write('listenonion=0\n') + fp.write('bind=127.0.0.1\n') + fp.write('debug=1\n') + fp.write('debugexclude=libevent\n') + + fp.write('fallbackfee=0.01\n') + fp.write('acceptnonstdtxn=0\n') + fp.write('txindex=1\n') + + fp.write('findpeers=0\n') + # minstakeinterval=5 # Using walletsettings stakelimit instead + fp.write('stakethreadconddelayms=1000\n') + + if base_p2p_port == BASE_PORT: # Particl + fp.write('zmqpubsmsg=tcp://127.0.0.1:{}\n'.format(BASE_ZMQ_PORT + node_id)) + + for i in range(0, num_nodes): + if node_id == i: + continue + fp.write('addnode=127.0.0.1:{}\n'.format(base_p2p_port + i)) + def checkForks(ro): if 'bip9_softforks' in ro: @@ -19,3 +80,117 @@ def checkForks(ro): else: assert(ro['softforks']['csv']['active']) assert(ro['softforks']['segwit']['active']) + + +def stopDaemons(daemons): + for d in daemons: + logging.info('Interrupting %d', d.pid) + try: + d.send_signal(signal.SIGINT) + except Exception as e: + logging.info('Interrupting %d, error %s', d.pid, str(e)) + for d in daemons: + try: + d.wait(timeout=20) + 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)) + + +def wait_for_bid(delay_event, swap_client, bid_id, state=None, sent=False, wait_for=20): + logging.info('wait_for_bid %s', bid_id.hex()) + for i in range(wait_for): + if delay_event.is_set(): + raise ValueError('Test stopped.') + delay_event.wait(1) + bids = swap_client.listBids(sent=sent) + for bid in bids: + if bid[1] == bid_id: + if state is not None and state != bid[4]: + continue + return + raise ValueError('wait_for_bid timed out.') + + +def wait_for_bid_tx_state(delay_event, swap_client, bid_id, initiate_state, participate_state, wait_for=30): + logging.info('wait_for_bid_tx_state %s %s %s', bid_id.hex(), str(initiate_state), str(participate_state)) + for i in range(wait_for): + if delay_event.is_set(): + raise ValueError('Test stopped.') + delay_event.wait(1) + bid = swap_client.getBid(bid_id) + if (initiate_state is None or bid.getITxState() == initiate_state) \ + and (participate_state is None or bid.getPTxState() == participate_state): + return + raise ValueError('wait_for_bid_tx_state timed out.') + + +def wait_for_offer(delay_event, swap_client, offer_id, wait_for=20): + logging.info('wait_for_offer %s', offer_id.hex()) + for i in range(wait_for): + if delay_event.is_set(): + raise ValueError('Test stopped.') + delay_event.wait(1) + offers = swap_client.listOffers() + for offer in offers: + if offer.offer_id == offer_id: + return + raise ValueError('wait_for_offer timed out.') + + +def wait_for_no_offer(delay_event, swap_client, offer_id, wait_for=20): + logging.info('wait_for_no_offer %s', offer_id.hex()) + for i in range(wait_for): + if delay_event.is_set(): + raise ValueError('Test stopped.') + delay_event.wait(1) + offers = swap_client.listOffers() + found_offer = False + for offer in offers: + if offer.offer_id == offer_id: + found_offer = True + break + if not found_offer: + return True + raise ValueError('wait_for_offer timed out.') + + +def wait_for_none_active(delay_event, port, wait_for=30): + for i in range(wait_for): + if delay_event.is_set(): + raise ValueError('Test stopped.') + delay_event.wait(1) + js = json.loads(urlopen('http://localhost:{}/json'.format(port)).read()) + if js['num_swapping'] == 0 and js['num_watched_outputs'] == 0: + return + raise ValueError('wait_for_none_active timed out.') + + +def wait_for_in_progress(delay_event, swap_client, bid_id, sent=False): + logging.info('wait_for_in_progress %s', bid_id.hex()) + for i in range(20): + if delay_event.is_set(): + raise ValueError('Test stopped.') + delay_event.wait(1) + swaps = swap_client.listSwapsInProgress() + for b in swaps: + if b[0] == bid_id: + return + raise ValueError('wait_for_in_progress timed out.') + + +def delay_for(delay_event, delay_for=60): + logging.info('Delaying for {} seconds.'.format(delay_for)) + delay_event.wait(delay_for) + + +def make_rpc_func(node_id, base_rpc_port=BASE_RPC_PORT): + node_id = node_id + auth = 'test{0}:test_pass{0}'.format(node_id) + + def rpc_func(method, params=None, wallet=None): + nonlocal node_id, auth + return callrpc(base_rpc_port + node_id, auth, method, params, wallet) + return rpc_func diff --git a/tests/basicswap/test_network.py b/tests/basicswap/test_network.py new file mode 100644 index 0000000..755ed14 --- /dev/null +++ b/tests/basicswap/test_network.py @@ -0,0 +1,306 @@ +#!/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. + +import os +import json +import time +import shutil +import signal +import logging +import unittest +import traceback +import threading +from urllib.request import urlopen + +import basicswap.config as cfg +from basicswap.basicswap import ( + BasicSwap, + Coins, + SwapTypes, +) +from basicswap.util import ( + COIN, + toWIF, +) +from basicswap.rpc import ( + callrpc, + callrpc_cli, + waitForRPC, +) +from basicswap.contrib.key import ( + ECKey, +) +from basicswap.http_server import ( + HttpThread, +) +from tests.basicswap.common import ( + prepareDataDir, + make_rpc_func, + checkForks, + stopDaemons, + wait_for_offer, + TEST_HTTP_HOST, + TEST_HTTP_PORT, + BASE_P2P_PORT, + BASE_RPC_PORT, + BASE_ZMQ_PORT, + BTC_BASE_PORT, + BTC_BASE_RPC_PORT, + PREFIX_SECRET_KEY_REGTEST, +) + +from bin.basicswap_run import startDaemon + + +logger = logging.getLogger() + +NUM_NODES = 3 +NUM_BTC_NODES = 3 +TEST_DIR = cfg.TEST_DATADIRS + +delay_event = threading.Event() +stop_test = False + + +def prepare_swapclient_dir(datadir, node_id, network_key, network_pubkey): + basicswap_dir = os.path.join(datadir, 'basicswap_' + str(node_id)) + if not os.path.exists(basicswap_dir): + os.makedirs(basicswap_dir) + + settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) + settings = { + 'debug': True, + 'p2p_host': '127.0.0.1', + 'p2p_port': BASE_P2P_PORT + node_id, + 'zmqhost': 'tcp://127.0.0.1', + 'zmqport': BASE_ZMQ_PORT + node_id, + 'htmlhost': 'localhost', + 'htmlport': TEST_HTTP_PORT + node_id, + 'network_key': network_key, + 'network_pubkey': network_pubkey, + 'chainclients': { + 'particl': { + 'connection_type': 'rpc', + 'manage_daemon': False, + 'rpcport': BASE_RPC_PORT + node_id, + 'rpcuser': 'test' + str(node_id), + 'rpcpassword': 'test_pass' + str(node_id), + 'datadir': os.path.join(datadir, 'part_' + str(node_id)), + 'bindir': cfg.PARTICL_BINDIR, + 'blocks_confirmed': 2, # Faster testing + }, + 'bitcoin': { + 'connection_type': 'rpc', + 'manage_daemon': False, + 'rpcport': BTC_BASE_RPC_PORT + node_id, + 'rpcuser': 'test' + str(node_id), + 'rpcpassword': 'test_pass' + str(node_id), + 'datadir': os.path.join(datadir, 'btc_' + str(node_id)), + 'bindir': cfg.BITCOIN_BINDIR, + 'use_segwit': True, + } + + }, + 'check_progress_seconds': 2, + 'check_watched_seconds': 4, + 'check_expired_seconds': 60, + 'check_events_seconds': 1, + 'check_xmr_swaps_seconds': 1, + 'min_delay_event': 1, + 'max_delay_event': 5, + 'min_delay_retry': 2, + 'max_delay_retry': 10 + } + + with open(settings_path, 'w') as fp: + json.dump(settings, fp, indent=4) + + +def partRpc(cmd, node_id=0): + return callrpc_cli(cfg.PARTICL_BINDIR, os.path.join(TEST_DIR, 'part_' + str(node_id)), 'regtest', cmd, cfg.PARTICL_CLI) + + +def btcRpc(cmd, node_id=0): + return callrpc_cli(cfg.BITCOIN_BINDIR, os.path.join(TEST_DIR, 'btc_' + str(node_id)), 'regtest', cmd, cfg.BITCOIN_CLI) + + +def signal_handler(sig, frame): + global stop_test + logging.info('signal {} detected.'.format(sig)) + stop_test = True + delay_event.set() + + +def callnoderpc(node_id, method, params=[], wallet=None, base_rpc_port=BASE_RPC_PORT): + auth = 'test{0}:test_pass{0}'.format(node_id) + return callrpc(base_rpc_port + node_id, auth, method, params, wallet) + + +def run_coins_loop(cls): + global stop_test + while not stop_test: + if cls.btc_addr is not None: + btcRpc('generatetoaddress 1 {}'.format(cls.btc_addr)) + time.sleep(1.0) + + +def run_loop(cls): + global stop_test + while not stop_test: + for c in cls.swap_clients: + c.update() + time.sleep(1.0) + + +class Test(unittest.TestCase): + + @classmethod + def setUpClass(cls): + super(Test, cls).setUpClass() + + cls.update_thread = None + cls.coins_update_thread = None + cls.http_threads = [] + cls.swap_clients = [] + cls.part_daemons = [] + cls.btc_daemons = [] + + cls.part_stakelimit = 0 + cls.btc_addr = None + + logger.propagate = False + logger.handlers = [] + logger.setLevel(logging.INFO) # DEBUG shows many messages from requests.post + formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s') + stream_stdout = logging.StreamHandler() + stream_stdout.setFormatter(formatter) + logger.addHandler(stream_stdout) + + if os.path.isdir(TEST_DIR): + logging.info('Removing ' + TEST_DIR) + shutil.rmtree(TEST_DIR) + if not os.path.exists(TEST_DIR): + os.makedirs(TEST_DIR) + + cls.stream_fp = logging.FileHandler(os.path.join(TEST_DIR, 'test.log')) + cls.stream_fp.setFormatter(formatter) + logger.addHandler(cls.stream_fp) + + try: + logging.info('Preparing coin nodes.') + for i in range(NUM_NODES): + prepareDataDir(TEST_DIR, i, 'particl.conf', 'part_') + + cls.part_daemons.append(startDaemon(os.path.join(TEST_DIR, 'part_' + str(i)), cfg.PARTICL_BINDIR, cfg.PARTICLD)) + logging.info('Started %s %d', cfg.PARTICLD, cls.part_daemons[-1].pid) + + for i in range(NUM_NODES): + # Load mnemonics after all nodes have started to avoid staking getting stuck in TryToSync + rpc = make_rpc_func(i) + waitForRPC(rpc) + if i == 0: + rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb']) + elif i == 1: + rpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true']) + rpc('getnewextaddress', ['lblExtTest']) + rpc('rescanblockchain') + else: + rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']]) + # Lower output split threshold for more stakeable outputs + rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}]) + + for i in range(NUM_BTC_NODES): + prepareDataDir(TEST_DIR, i, 'bitcoin.conf', 'btc_', base_p2p_port=BTC_BASE_PORT, base_rpc_port=BTC_BASE_RPC_PORT) + + cls.btc_daemons.append(startDaemon(os.path.join(TEST_DIR, 'btc_' + str(i)), cfg.BITCOIN_BINDIR, cfg.BITCOIND)) + logging.info('Started %s %d', cfg.BITCOIND, cls.part_daemons[-1].pid) + + waitForRPC(make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT)) + + logging.info('Preparing swap clients.') + eckey = ECKey() + eckey.generate() + cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes()) + cls.network_pubkey = eckey.get_pubkey().get_bytes().hex() + + for i in range(NUM_NODES): + prepare_swapclient_dir(TEST_DIR, i, cls.network_key, cls.network_pubkey) + basicswap_dir = os.path.join(os.path.join(TEST_DIR, 'basicswap_' + str(i))) + settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) + with open(settings_path) as fs: + settings = json.load(fs) + fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w') + sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i)) + sc.setDaemonPID(Coins.BTC, cls.btc_daemons[i].pid) + sc.setDaemonPID(Coins.PART, cls.part_daemons[i].pid) + sc.start() + cls.swap_clients.append(sc) + + t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i]) + cls.http_threads.append(t) + t.start() + + cls.btc_addr = callnoderpc(0, 'getnewaddress', ['mining_addr', 'bech32'], base_rpc_port=BTC_BASE_RPC_PORT) + + num_blocks = 500 + logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr) + callnoderpc(0, 'generatetoaddress', [num_blocks, cls.btc_addr], base_rpc_port=BTC_BASE_RPC_PORT) + + checkForks(callnoderpc(0, 'getblockchaininfo', base_rpc_port=BTC_BASE_RPC_PORT)) + + logging.info('Starting update thread.') + signal.signal(signal.SIGINT, signal_handler) + cls.update_thread = threading.Thread(target=run_loop, args=(cls,)) + cls.update_thread.start() + + cls.coins_update_thread = threading.Thread(target=run_coins_loop, args=(cls,)) + cls.coins_update_thread.start() + except Exception: + traceback.print_exc() + Test.tearDownClass() + raise ValueError('setUpClass() failed.') + + @classmethod + def tearDownClass(cls): + global stop_test + logging.info('Finalising') + stop_test = True + if cls.update_thread is not None: + try: + cls.update_thread.join() + except Exception: + logging.info('Failed to join update_thread') + if cls.coins_update_thread is not None: + try: + cls.coins_update_thread.join() + except Exception: + logging.info('Failed to join coins_update_thread') + + for t in cls.http_threads: + t.stop() + t.join() + for c in cls.swap_clients: + c.finalise() + c.fp.close() + + stopDaemons(cls.part_daemons) + stopDaemons(cls.btc_daemons) + + super(Test, cls).tearDownClass() + + def test_01_part_btc(self): + logging.info('---------- Test PART to BTC') + swap_clients = self.swap_clients + + js_1 = json.loads(urlopen('http://localhost:1801/json/wallets').read()) + + offer_id = swap_clients[0].postOffer(Coins.PART, Coins.BTC, 100 * COIN, 0.1 * COIN, 100 * COIN, SwapTypes.SELLER_FIRST) + wait_for_offer(delay_event, swap_clients[1], offer_id) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/basicswap/test_nmc.py b/tests/basicswap/test_nmc.py index 0686ebd..6c8c0f6 100644 --- a/tests/basicswap/test_nmc.py +++ b/tests/basicswap/test_nmc.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- 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. @@ -21,6 +21,7 @@ import signal import threading from urllib.request import urlopen +import basicswap.config as cfg from basicswap.basicswap import ( BasicSwap, Coins, @@ -45,12 +46,21 @@ from basicswap.http_server import ( HttpThread, ) from tests.basicswap.common import ( + checkForks, + stopDaemons, + wait_for_offer, + wait_for_bid, + wait_for_bid_tx_state, + wait_for_in_progress, TEST_HTTP_HOST, TEST_HTTP_PORT, + BASE_PORT, + BASE_RPC_PORT, + BASE_ZMQ_PORT, + PREFIX_SECRET_KEY_REGTEST, ) from bin.basicswap_run import startDaemon -import basicswap.config as cfg logger = logging.getLogger() logger.level = logging.DEBUG @@ -58,12 +68,10 @@ if not len(logger.handlers): logger.addHandler(logging.StreamHandler(sys.stdout)) NUM_NODES = 3 -BASE_PORT = 14792 -BASE_RPC_PORT = 19792 -BASE_ZMQ_PORT = 20792 -PREFIX_SECRET_KEY_REGTEST = 0x2e NMC_NODE = 3 BTC_NODE = 4 + +delay_event = threading.Event() stop_test = False @@ -193,6 +201,7 @@ def signal_handler(sig, frame): global stop_test print('signal {} detected.'.format(sig)) stop_test = True + delay_event.set() def run_loop(self): @@ -204,6 +213,19 @@ def run_loop(self): btcRpc('generatetoaddress 1 {}'.format(self.btc_addr)) +def make_part_cli_rpc_func(node_id): + node_id = node_id + + def rpc_func(method, params=None, wallet=None): + nonlocal node_id + cmd = method + if params: + for p in params: + cmd += ' "' + p + '"' + return partRpc(cmd, node_id) + return rpc_func + + class Test(unittest.TestCase): @classmethod @@ -227,6 +249,7 @@ class Test(unittest.TestCase): cls.daemons = [] cls.swap_clients = [] + cls.http_threads = [] cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(BTC_NODE)), cfg.BITCOIN_BINDIR, cfg.BITCOIND)) logging.info('Started %s %d', cfg.BITCOIND, cls.daemons[-1].pid) @@ -234,8 +257,20 @@ class Test(unittest.TestCase): logging.info('Started %s %d', cfg.NAMECOIND, cls.daemons[-1].pid) for i in range(NUM_NODES): - cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(i))), cfg.PARTICL_BINDIR, cfg.PARTICLD) + cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(i)), cfg.PARTICL_BINDIR, cfg.PARTICLD)) logging.info('Started %s %d', cfg.PARTICLD, cls.daemons[-1].pid) + + rpc = make_part_cli_rpc_func(i) + waitForRPC(rpc) + if i == 0: + rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb']) + elif i == 1: + rpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true']) + rpc('getnewextaddress', ['lblExtTest']) + rpc('rescanblockchain') + else: + rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']]) + time.sleep(1) for i in range(NUM_NODES): basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap') @@ -248,10 +283,10 @@ class Test(unittest.TestCase): cls.swap_clients[-1].setDaemonPID(Coins.NMC, cls.daemons[1].pid) cls.swap_clients[-1].setDaemonPID(Coins.PART, cls.daemons[2 + i].pid) cls.swap_clients[-1].start() - cls.swap_clients[0].callrpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb']) - cls.swap_clients[1].callrpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true']) - cls.swap_clients[1].callrpc('getnewextaddress', ['lblExtTest']) - cls.swap_clients[1].callrpc('rescanblockchain') + + t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i]) + cls.http_threads.append(t) + t.start() waitForRPC(nmcRpc) num_blocks = 500 @@ -275,18 +310,11 @@ class Test(unittest.TestCase): btcRpc('generatetoaddress {} {}'.format(num_blocks, cls.btc_addr)) ro = btcRpc('getblockchaininfo') - assert(ro['bip9_softforks']['csv']['status'] == 'active') - assert(ro['bip9_softforks']['segwit']['status'] == 'active') + checkForks(ro) ro = nmcRpc('getwalletinfo') print('nmcRpc', ro) - cls.http_threads = [] - for i in range(3): - t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i]) - cls.http_threads.append(t) - t.start() - signal.signal(signal.SIGINT, signal_handler) cls.update_thread = threading.Thread(target=run_loop, args=(cls,)) cls.update_thread.start() @@ -301,68 +329,12 @@ class Test(unittest.TestCase): t.stop() t.join() for c in cls.swap_clients: + c.finalise() c.fp.close() - for d in cls.daemons: - logging.info('Terminating %d', d.pid) - d.terminate() - d.wait(timeout=10) - if d.stdout: - d.stdout.close() - if d.stderr: - d.stderr.close() - if d.stdin: - d.stdin.close() - super(Test, cls).tearDownClass() + stopDaemons(cls.daemons) - def wait_for_offer(self, swap_client, offer_id): - logging.info('wait_for_offer %s', offer_id.hex()) - for i in range(20): - time.sleep(1) - offers = swap_client.listOffers() - for offer in offers: - if offer.offer_id == offer_id: - return - raise ValueError('wait_for_offer timed out.') - - def wait_for_bid(self, swap_client, bid_id): - logging.info('wait_for_bid %s', bid_id.hex()) - for i in range(20): - time.sleep(1) - bids = swap_client.listBids() - for bid in bids: - if bid[1] == bid_id and int(bid[5]) == 1: - return - raise ValueError('wait_for_bid timed out.') - - def wait_for_in_progress(self, swap_client, bid_id, sent=False): - logging.info('wait_for_in_progress %s', bid_id.hex()) - for i in range(20): - time.sleep(1) - swaps = swap_client.listSwapsInProgress() - for b in swaps: - if b[0] == bid_id: - return - raise ValueError('wait_for_in_progress timed out.') - - def wait_for_bid_state(self, swap_client, bid_id, state, sent=False, seconds_for=30): - logging.info('wait_for_bid_state %s %s', bid_id.hex(), str(state)) - for i in range(seconds_for): - time.sleep(1) - bid = swap_client.getBid(bid_id) - if bid.state >= state: - return - raise ValueError('wait_for_bid_state timed out.') - - def wait_for_bid_tx_state(self, swap_client, bid_id, initiate_state, participate_state, seconds_for=30): - logging.info('wait_for_bid_tx_state %s %s %s', bid_id.hex(), str(initiate_state), str(participate_state)) - for i in range(seconds_for): - time.sleep(1) - bid = swap_client.getBid(bid_id) - if (initiate_state is None or bid.getITxState() == initiate_state) \ - and (participate_state is None or bid.getPTxState() == participate_state): - return - raise ValueError('wait_for_bid_tx_state timed out.') + super(Test, cls).tearDownClass() def test_02_part_ltc(self): logging.info('---------- Test PART to NMC') @@ -370,21 +342,21 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer(Coins.PART, Coins.NMC, 100 * COIN, 0.1 * COIN, 100 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers() assert(len(offers) == 1) for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id) + wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - self.wait_for_in_progress(swap_clients[1], bid_id, sent=True) + wait_for_in_progress(delay_event, swap_clients[1], bid_id, sent=True) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) - self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60) js_0 = json.loads(urlopen('http://localhost:1800/json').read()) js_1 = json.loads(urlopen('http://localhost:1801/json').read()) @@ -397,19 +369,19 @@ class Test(unittest.TestCase): offer_id = swap_clients[1].postOffer(Coins.NMC, Coins.PART, 10 * COIN, 9.0 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME) - self.wait_for_offer(swap_clients[0], offer_id) + wait_for_offer(delay_event, swap_clients[0], offer_id) offers = swap_clients[0].listOffers() for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[0].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[1], bid_id) + wait_for_bid(delay_event, swap_clients[1], bid_id) swap_clients[1].acceptBid(bid_id) - self.wait_for_in_progress(swap_clients[0], bid_id, sent=True) + wait_for_in_progress(delay_event, swap_clients[0], bid_id, sent=True) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, sent=True, seconds_for=60) - self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) js_0 = json.loads(urlopen('http://localhost:1800/json').read()) js_1 = json.loads(urlopen('http://localhost:1801/json').read()) @@ -422,19 +394,19 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers() for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id) + wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - self.wait_for_in_progress(swap_clients[1], bid_id, sent=True) + wait_for_in_progress(delay_event, swap_clients[1], bid_id, sent=True) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) - self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60) js_0bid = json.loads(urlopen('http://localhost:1800/json/bids/{}'.format(bid_id.hex())).read()) @@ -452,18 +424,18 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_BLOCKS, 10) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers() for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id) + wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[1].abandonBid(bid_id) swap_clients[0].acceptBid(bid_id) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) - self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.BID_ABANDONED, sent=True, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.BID_ABANDONED, sent=True, wait_for=60) js_0 = json.loads(urlopen('http://localhost:1800/json').read()) js_1 = json.loads(urlopen('http://localhost:1801/json').read()) @@ -478,17 +450,17 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 10 * COIN, 10 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME) - self.wait_for_offer(swap_clients[0], offer_id) + wait_for_offer(delay_event, swap_clients[0], offer_id) offers = swap_clients[0].listOffers() for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[0].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id) + wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - self.wait_for_bid_tx_state(swap_clients[0], bid_id, TxStates.TX_REDEEMED, TxStates.TX_REDEEMED, seconds_for=60) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) + wait_for_bid_tx_state(delay_event, swap_clients[0], bid_id, TxStates.TX_REDEEMED, TxStates.TX_REDEEMED, wait_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) js_0 = json.loads(urlopen('http://localhost:1800/json').read()) assert(js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0) @@ -502,18 +474,18 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 0.001 * COIN, 1.0 * COIN, 0.001 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME) - self.wait_for_offer(swap_clients[0], offer_id) + wait_for_offer(delay_event, swap_clients[0], offer_id) offers = swap_clients[0].listOffers() for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[0].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id) + wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) swap_clients[0].coin_clients[Coins.BTC]['override_feerate'] = 10.0 swap_clients[0].coin_clients[Coins.NMC]['override_feerate'] = 10.0 - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.BID_ERROR, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) def pass_99_delay(self): global stop_test diff --git a/tests/basicswap/test_run.py b/tests/basicswap/test_run.py index 37edfee..7196e2b 100644 --- a/tests/basicswap/test_run.py +++ b/tests/basicswap/test_run.py @@ -15,12 +15,12 @@ $ python setup.py test -s tests.basicswap.test_run.Test.test_04_ltc_btc import os import sys -import unittest import json -import logging -import shutil import time +import shutil import signal +import logging +import unittest import threading from urllib.request import urlopen @@ -50,26 +50,35 @@ from basicswap.http_server import ( ) from tests.basicswap.common import ( checkForks, + stopDaemons, + wait_for_offer, + wait_for_bid, + wait_for_bid_tx_state, + wait_for_in_progress, TEST_HTTP_HOST, TEST_HTTP_PORT, + BASE_PORT, + BASE_RPC_PORT, + BASE_ZMQ_PORT, + PREFIX_SECRET_KEY_REGTEST, ) from bin.basicswap_run import startDaemon -logger = logging.getLogger() -logger.level = logging.DEBUG -if not len(logger.handlers): - logger.addHandler(logging.StreamHandler(sys.stdout)) NUM_NODES = 3 -BASE_PORT = 14792 -BASE_RPC_PORT = 19792 -BASE_ZMQ_PORT = 20792 -PREFIX_SECRET_KEY_REGTEST = 0x2e LTC_NODE = 3 BTC_NODE = 4 + +delay_event = threading.Event() stop_test = False +logger = logging.getLogger() +logger.level = logging.DEBUG +if not len(logger.handlers): + logger.addHandler(logging.StreamHandler(sys.stdout)) + + def prepareOtherDir(datadir, nodeId, conf_file='litecoin.conf'): node_dir = os.path.join(datadir, str(nodeId)) if not os.path.exists(node_dir): @@ -120,7 +129,8 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey): fp.write('zmqpubsmsg=tcp://127.0.0.1:' + str(BASE_ZMQ_PORT + nodeId) + '\n') fp.write('acceptnonstdtxn=0\n') - fp.write('minstakeinterval=5\n') + fp.write('minstakeinterval=2\n') + fp.write('stakethreadconddelayms=1000\n') for i in range(0, NUM_NODES): if nodeId == i: @@ -199,6 +209,7 @@ def signal_handler(sig, frame): global stop_test print('signal {} detected.'.format(sig)) stop_test = True + delay_event.set() def run_loop(self): @@ -210,6 +221,24 @@ def run_loop(self): btcRpc('generatetoaddress 1 {}'.format(self.btc_addr)) +def make_part_cli_rpc_func(node_id): + node_id = node_id + + def rpc_func(method, params=None, wallet=None): + nonlocal node_id + cmd = method + if params: + for p in params: + if isinstance(p, dict) or isinstance(p, list): + cmd += ' "' + dumpje(p) + '"' + elif isinstance(p, int): + cmd += ' ' + str(p) + else: + cmd += ' "' + p + '"' + return partRpc(cmd, node_id) + return rpc_func + + class Test(unittest.TestCase): @classmethod @@ -233,6 +262,7 @@ class Test(unittest.TestCase): cls.daemons = [] cls.swap_clients = [] + cls.http_threads = [] cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(BTC_NODE)), cfg.BITCOIN_BINDIR, cfg.BITCOIND)) logging.info('Started %s %d', cfg.BITCOIND, cls.daemons[-1].pid) @@ -242,8 +272,22 @@ class Test(unittest.TestCase): for i in range(NUM_NODES): cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(i)), cfg.PARTICL_BINDIR, cfg.PARTICLD)) logging.info('Started %s %d', cfg.PARTICLD, cls.daemons[-1].pid) - time.sleep(1) + for i in range(NUM_NODES): + # Load mnemonics after all nodes have started to avoid staking getting stuck in TryToSync + rpc = make_part_cli_rpc_func(i) + waitForRPC(rpc) + if i == 0: + rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb']) + elif i == 1: + rpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true']) + rpc('getnewextaddress', ['lblExtTest']) + rpc('rescanblockchain') + else: + rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']]) + # Lower output split threshold for more stakeable outputs + rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}]) + basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap') settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) with open(settings_path) as fs: @@ -255,10 +299,10 @@ class Test(unittest.TestCase): sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].pid) sc.start() cls.swap_clients.append(sc) - cls.swap_clients[0].callrpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb']) - cls.swap_clients[1].callrpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true']) - cls.swap_clients[1].callrpc('getnewextaddress', ['lblExtTest']) - cls.swap_clients[1].callrpc('rescanblockchain') + + t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i]) + cls.http_threads.append(t) + t.start() waitForRPC(ltcRpc) num_blocks = 500 @@ -280,16 +324,21 @@ class Test(unittest.TestCase): ro = ltcRpc('getwalletinfo') print('ltcRpc', ro) - cls.http_threads = [] - for i in range(3): - t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i]) - cls.http_threads.append(t) - t.start() - signal.signal(signal.SIGINT, signal_handler) cls.update_thread = threading.Thread(target=run_loop, args=(cls,)) cls.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): + particl_blocks = cls.swap_clients[0].callrpc('getblockchaininfo')['blocks'] + print('particl_blocks', particl_blocks) + if particl_blocks >= num_blocks: + break + delay_event.wait(1) + assert(particl_blocks >= num_blocks) + @classmethod def tearDownClass(cls): global stop_test @@ -300,72 +349,12 @@ class Test(unittest.TestCase): t.stop() t.join() for c in cls.swap_clients: + c.finalise() c.fp.close() - for d in cls.daemons: - logging.info('Interrupting %d', d.pid) - try: - d.send_signal(signal.SIGINT) - except Exception as e: - logging.info('Interrupting %d, error %s', d.pid, str(e)) - for d in cls.daemons: - d.wait(timeout=10) - if d.stdout: - d.stdout.close() - if d.stderr: - d.stderr.close() - if d.stdin: - d.stdin.close() - super(Test, cls).tearDownClass() + stopDaemons(cls.daemons) - def wait_for_offer(self, swap_client, offer_id): - logging.info('wait_for_offer %s', offer_id.hex()) - for i in range(20): - time.sleep(1) - offers = swap_client.listOffers() - for offer in offers: - if offer.offer_id == offer_id: - return - raise ValueError('wait_for_offer timed out.') - - def wait_for_bid(self, swap_client, bid_id): - logging.info('wait_for_bid %s', bid_id.hex()) - for i in range(20): - time.sleep(1) - bids = swap_client.listBids() - for bid in bids: - if bid[1] == bid_id and int(bid[5]) == 1: - return - raise ValueError('wait_for_bid timed out.') - - def wait_for_in_progress(self, swap_client, bid_id, sent=False): - logging.info('wait_for_in_progress %s', bid_id.hex()) - for i in range(20): - time.sleep(1) - swaps = swap_client.listSwapsInProgress() - for b in swaps: - if b[0] == bid_id: - return - raise ValueError('wait_for_in_progress timed out.') - - def wait_for_bid_state(self, swap_client, bid_id, state, sent=False, seconds_for=30): - logging.info('wait_for_bid_state %s %s', bid_id.hex(), str(state)) - for i in range(seconds_for): - time.sleep(1) - bid = swap_client.getBid(bid_id) - if bid and bid.state >= state: - return - raise ValueError('wait_for_bid_state timed out.') - - def wait_for_bid_tx_state(self, swap_client, bid_id, initiate_state, participate_state, seconds_for=30): - logging.info('wait_for_bid_tx_state %s %s %s', bid_id.hex(), str(initiate_state), str(participate_state)) - for i in range(seconds_for): - time.sleep(1) - bid = swap_client.getBid(bid_id) - if (initiate_state is None or bid.getITxState() == initiate_state) \ - and (participate_state is None or bid.getPTxState() == participate_state): - return - raise ValueError('wait_for_bid_tx_state timed out.') + super(Test, cls).tearDownClass() def test_01_verifyrawtransaction(self): txn = '0200000001eb6e5c4ebba4efa32f40c7314cad456a64008e91ee30b2dd0235ab9bb67fbdbb01000000ee47304402200956933242dde94f6cf8f195a470f8d02aef21ec5c9b66c5d3871594bdb74c9d02201d7e1b440de8f4da672d689f9e37e98815fb63dbc1706353290887eb6e8f7235012103dc1b24feb32841bc2f4375da91fa97834e5983668c2a39a6b7eadb60e7033f9d205a803b28fe2f86c17db91fa99d7ed2598f79b5677ffe869de2e478c0d1c02cc7514c606382012088a8201fe90717abb84b481c2a59112414ae56ec8acc72273642ca26cc7a5812fdc8f68876a914225fbfa4cb725b75e511810ac4d6f74069bdded26703520140b27576a914207eb66b2fd6ed9924d6217efc7fa7b38dfabe666888acffffffff01e0167118020000001976a9140044e188928710cecba8311f1cf412135b98145c88ac00000000' @@ -406,21 +395,21 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer(Coins.PART, Coins.LTC, 100 * COIN, 0.1 * COIN, 100 * COIN, SwapTypes.SELLER_FIRST) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers() assert(len(offers) == 1) for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id) + wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - self.wait_for_in_progress(swap_clients[1], bid_id, sent=True) + wait_for_in_progress(delay_event, swap_clients[1], bid_id, sent=True) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) - self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60) js_0 = json.loads(urlopen('http://localhost:1800/json').read()) js_1 = json.loads(urlopen('http://localhost:1801/json').read()) @@ -433,19 +422,19 @@ class Test(unittest.TestCase): offer_id = swap_clients[1].postOffer(Coins.LTC, Coins.PART, 10 * COIN, 9.0 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST) - self.wait_for_offer(swap_clients[0], offer_id) + wait_for_offer(delay_event, swap_clients[0], offer_id) offers = swap_clients[0].listOffers() for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[0].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[1], bid_id) + wait_for_bid(delay_event, swap_clients[1], bid_id) swap_clients[1].acceptBid(bid_id) - self.wait_for_in_progress(swap_clients[0], bid_id, sent=True) + wait_for_in_progress(delay_event, swap_clients[0], bid_id, sent=True) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, sent=True, seconds_for=60) - self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) js_0 = json.loads(urlopen('http://localhost:1800/json').read()) js_1 = json.loads(urlopen('http://localhost:1801/json').read()) @@ -458,19 +447,19 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer(Coins.LTC, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers() for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id) + wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - self.wait_for_in_progress(swap_clients[1], bid_id, sent=True) + wait_for_in_progress(delay_event, swap_clients[1], bid_id, sent=True) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) - self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60) js_0bid = json.loads(urlopen('http://localhost:1800/json/bids/{}'.format(bid_id.hex())).read()) @@ -488,18 +477,18 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer(Coins.LTC, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, SEQUENCE_LOCK_BLOCKS, 10) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers() for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id) + wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[1].abandonBid(bid_id) swap_clients[0].acceptBid(bid_id) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) - self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.BID_ABANDONED, sent=True, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.BID_ABANDONED, sent=True, wait_for=60) js_0 = json.loads(urlopen('http://localhost:1800/json').read()) js_1 = json.loads(urlopen('http://localhost:1801/json').read()) @@ -514,17 +503,17 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.LTC, 10 * COIN, 10 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST) - self.wait_for_offer(swap_clients[0], offer_id) + wait_for_offer(delay_event, swap_clients[0], offer_id) offers = swap_clients[0].listOffers() for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[0].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id) + wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - self.wait_for_bid_tx_state(swap_clients[0], bid_id, TxStates.TX_REDEEMED, TxStates.TX_REDEEMED, seconds_for=60) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) + wait_for_bid_tx_state(delay_event, swap_clients[0], bid_id, TxStates.TX_REDEEMED, TxStates.TX_REDEEMED, wait_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) js_0 = json.loads(urlopen('http://localhost:1800/json').read()) assert(js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0) @@ -538,18 +527,18 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.LTC, 0.001 * COIN, 1.0 * COIN, 0.001 * COIN, SwapTypes.SELLER_FIRST) - self.wait_for_offer(swap_clients[0], offer_id) + wait_for_offer(delay_event, swap_clients[0], offer_id) offers = swap_clients[0].listOffers() for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[0].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id) + wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) swap_clients[0].coin_clients[Coins.BTC]['override_feerate'] = 10.0 swap_clients[0].coin_clients[Coins.LTC]['override_feerate'] = 10.0 - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.BID_ERROR, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) swap_clients[0].abandonBid(bid_id) del swap_clients[0].coin_clients[Coins.BTC]['override_feerate'] @@ -563,21 +552,21 @@ class Test(unittest.TestCase): return # TODO - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers() assert(len(offers) == 1) for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id) + wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - self.wait_for_in_progress(swap_clients[1], bid_id, sent=True) + wait_for_in_progress(delay_event, swap_clients[1], bid_id, sent=True) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) - self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60) js_0 = json.loads(urlopen('http://localhost:1800/json').read()) js_1 = json.loads(urlopen('http://localhost:1801/json').read()) @@ -590,15 +579,15 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer(Coins.PART, Coins.LTC, 100 * COIN, 0.1 * COIN, 100 * COIN, SwapTypes.SELLER_FIRST, auto_accept_bids=True) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers() assert(len(offers) >= 1) for offer in offers: if offer.offer_id == offer_id: bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) - self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60) - self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, seconds_for=60) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60) def pass_99_delay(self): global stop_test diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py index ddecbc8..3dbdce7 100644 --- a/tests/basicswap/test_xmr.py +++ b/tests/basicswap/test_xmr.py @@ -50,11 +50,22 @@ from basicswap.http_server import ( HttpThread, ) from tests.basicswap.common import ( + prepareDataDir, + make_rpc_func, checkForks, + stopDaemons, + wait_for_bid, + wait_for_offer, + wait_for_no_offer, + wait_for_none_active, TEST_HTTP_HOST, TEST_HTTP_PORT, + BASE_RPC_PORT, + BASE_ZMQ_PORT, + BTC_BASE_PORT, + BTC_BASE_RPC_PORT, + PREFIX_SECRET_KEY_REGTEST, ) -from basicswap.contrib.rpcauth import generate_salt, password_to_hmac from bin.basicswap_run import startDaemon, startXmrDaemon @@ -65,21 +76,11 @@ NUM_XMR_NODES = 3 NUM_BTC_NODES = 3 TEST_DIR = cfg.TEST_DATADIRS -BASE_PORT = 14792 -BASE_RPC_PORT = 19792 -BASE_ZMQ_PORT = 20792 - -BTC_BASE_PORT = 31792 -BTC_BASE_RPC_PORT = 32792 -BTC_BASE_ZMQ_PORT = 33792 - XMR_BASE_P2P_PORT = 17792 XMR_BASE_RPC_PORT = 21792 XMR_BASE_ZMQ_PORT = 22792 XMR_BASE_WALLET_RPC_PORT = 23792 -PREFIX_SECRET_KEY_REGTEST = 0x2e - delay_event = threading.Event() stop_test = False @@ -111,47 +112,6 @@ def prepareXmrDataDir(datadir, node_id, conf_file): fp.write('add-exclusive-node=127.0.0.1:{}\n'.format(XMR_BASE_P2P_PORT + i)) -def prepareDataDir(datadir, node_id, conf_file, dir_prefix, base_p2p_port=BASE_PORT, base_rpc_port=BASE_RPC_PORT): - node_dir = os.path.join(datadir, dir_prefix + str(node_id)) - if not os.path.exists(node_dir): - os.makedirs(node_dir) - cfg_file_path = os.path.join(node_dir, conf_file) - if os.path.exists(cfg_file_path): - return - with open(cfg_file_path, 'w+') as fp: - fp.write('regtest=1\n') - fp.write('[regtest]\n') - fp.write('port=' + str(base_p2p_port + node_id) + '\n') - fp.write('rpcport=' + str(base_rpc_port + node_id) + '\n') - - salt = generate_salt(16) - fp.write('rpcauth={}:{}${}\n'.format('test' + str(node_id), salt, password_to_hmac(salt, 'test_pass' + str(node_id)))) - - fp.write('daemon=0\n') - fp.write('printtoconsole=0\n') - fp.write('server=1\n') - fp.write('discover=0\n') - fp.write('listenonion=0\n') - fp.write('bind=127.0.0.1\n') - fp.write('debug=1\n') - fp.write('debugexclude=libevent\n') - - fp.write('fallbackfee=0.01\n') - fp.write('acceptnonstdtxn=0\n') - fp.write('txindex=1\n') - - fp.write('findpeers=0\n') - # minstakeinterval=5 # Using walletsettings stakelimit instead - - if base_p2p_port == BASE_PORT: # Particl - fp.write('zmqpubsmsg=tcp://127.0.0.1:{}\n'.format(BASE_ZMQ_PORT + node_id)) - - for i in range(0, NUM_NODES): - if node_id == i: - continue - fp.write('addnode=127.0.0.1:{}\n'.format(base_p2p_port + i)) - - def startXmrWalletRPC(node_dir, bin_dir, wallet_bin, node_id, opts=[]): daemon_bin = os.path.expanduser(os.path.join(bin_dir, wallet_bin)) @@ -236,24 +196,10 @@ def prepare_swapclient_dir(datadir, node_id, network_key, network_pubkey): json.dump(settings, fp, indent=4) -def partRpc(cmd, node_id=0): - return callrpc_cli(cfg.PARTICL_BINDIR, os.path.join(TEST_DIR, 'part_' + str(node_id)), 'regtest', cmd, cfg.PARTICL_CLI) - - def btcRpc(cmd, node_id=0): return callrpc_cli(cfg.BITCOIN_BINDIR, os.path.join(TEST_DIR, 'btc_' + str(node_id)), 'regtest', cmd, cfg.BITCOIN_CLI) -def make_rpc_func(node_id, base_rpc_port=BASE_RPC_PORT): - node_id = node_id - auth = 'test{0}:test_pass{0}'.format(node_id) - - def rpc_func(method, params=None, wallet=None): - nonlocal node_id, auth - return callrpc(base_rpc_port + node_id, auth, method, params, wallet) - return rpc_func - - def signal_handler(sig, frame): global stop_test logging.info('signal {} detected.'.format(sig)) @@ -354,7 +300,20 @@ class Test(unittest.TestCase): cls.part_daemons.append(startDaemon(os.path.join(TEST_DIR, 'part_' + str(i)), cfg.PARTICL_BINDIR, cfg.PARTICLD)) logging.info('Started %s %d', cfg.PARTICLD, cls.part_daemons[-1].pid) - waitForRPC(make_rpc_func(i)) + for i in range(NUM_NODES): + # Load mnemonics after all nodes have started to avoid staking getting stuck in TryToSync + rpc = make_rpc_func(i) + waitForRPC(rpc) + if i == 0: + rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb']) + elif i == 1: + rpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true']) + rpc('getnewextaddress', ['lblExtTest']) + rpc('rescanblockchain') + else: + rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']]) + # Lower output split threshold for more stakeable outputs + rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}]) for i in range(NUM_BTC_NODES): prepareDataDir(TEST_DIR, i, 'bitcoin.conf', 'btc_', base_p2p_port=BTC_BASE_PORT, base_rpc_port=BTC_BASE_RPC_PORT) @@ -401,13 +360,6 @@ class Test(unittest.TestCase): sc.start() cls.swap_clients.append(sc) - logging.info('Initialising coin networks.') - cls.swap_clients[0].callrpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb']) - cls.swap_clients[1].callrpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true']) - cls.swap_clients[1].callrpc('getnewextaddress', ['lblExtTest']) - cls.swap_clients[1].callrpc('rescanblockchain') - - for i in range(3): t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i]) cls.http_threads.append(t) t.start() @@ -459,100 +411,18 @@ class Test(unittest.TestCase): t.stop() t.join() for c in cls.swap_clients: + c.finalise() c.fp.close() - for d in cls.xmr_daemons: - logging.info('Interrupting %d', d.pid) - try: - d.send_signal(signal.SIGINT) - except Exception as e: - logging.info('Interrupting %d, error %s', d.pid, str(e)) - for d in cls.xmr_daemons: - try: - d.wait(timeout=20) - 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)) - - for d in cls.part_daemons + cls.btc_daemons: - logging.info('Interrupting %d', d.pid) - try: - d.send_signal(signal.SIGINT) - except Exception as e: - logging.info('Interrupting %d, error %s', d.pid, str(e)) - for d in cls.part_daemons + cls.btc_daemons: - try: - d.wait(timeout=20) - 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)) + stopDaemons(cls.xmr_daemons) + stopDaemons(cls.part_daemons) + stopDaemons(cls.btc_daemons) super(Test, cls).tearDownClass() def callxmrnodewallet(self, node_id, method, params=None): return callrpc_xmr(XMR_BASE_WALLET_RPC_PORT + node_id, self.xmr_wallet_auth[node_id], method, params) - def wait_for_offer(self, swap_client, offer_id, wait_for=20): - logging.info('wait_for_offer %s', offer_id.hex()) - for i in range(wait_for): - if stop_test: - raise ValueError('Test stopped.') - time.sleep(1) - offers = swap_client.listOffers() - for offer in offers: - if offer.offer_id == offer_id: - return - raise ValueError('wait_for_offer timed out.') - - def wait_for_no_offer(self, swap_client, offer_id, wait_for=20): - logging.info('wait_for_no_offer %s', offer_id.hex()) - for i in range(wait_for): - if stop_test: - raise ValueError('Test stopped.') - time.sleep(1) - offers = swap_client.listOffers() - found_offer = False - for offer in offers: - if offer.offer_id == offer_id: - found_offer = True - break - if not found_offer: - return True - raise ValueError('wait_for_offer timed out.') - - def wait_for_bid(self, swap_client, bid_id, state=None, sent=False, wait_for=20): - logging.info('wait_for_bid %s', bid_id.hex()) - for i in range(wait_for): - if stop_test: - raise ValueError('Test stopped.') - time.sleep(1) - bids = swap_client.listBids(sent=sent) - for bid in bids: - if bid[1] == bid_id: - if state is not None and state != bid[4]: - continue - return - raise ValueError('wait_for_bid timed out.') - - def wait_for_none_active(self, port, wait_for=30): - for i in range(wait_for): - if stop_test: - raise ValueError('Test stopped.') - time.sleep(1) - js = json.loads(urlopen('http://localhost:{}/json'.format(port)).read()) - if js['num_swapping'] == 0 and js['num_watched_outputs'] == 0: - return - raise ValueError('wait_for_none_active timed out.') - - def delay_for(self, delay_for=60): - logging.info('Delaying for {} seconds.'.format(delay_for)) - delay_event.clear() - delay_event.wait(delay_for) - def test_01_part_xmr(self): logging.info('---------- Test PART to XMR') swap_clients = self.swap_clients @@ -562,22 +432,22 @@ class Test(unittest.TestCase): assert(make_int(js_1[str(int(Coins.XMR))]['unconfirmed'], scale=12) > 0) offer_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, 100 * COIN, 0.11 * XMR_COIN, 100 * COIN, SwapTypes.XMR_SWAP) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers(filters={'offer_id': offer_id}) assert(len(offers) == 1) offer = offers[0] bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.BID_RECEIVED) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED) bid, xmr_swap = swap_clients[0].getXmrBid(bid_id) assert(xmr_swap) swap_clients[0].acceptXmrBid(bid_id) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180) - self.wait_for_bid(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True) js_0_end = json.loads(urlopen('http://localhost:1800/json/wallets').read()) end_xmr = float(js_0_end['6']['balance']) + float(js_0_end['6']['unconfirmed']) @@ -592,12 +462,12 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer( Coins.PART, Coins.XMR, 101 * COIN, 0.12 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP, lock_type=SEQUENCE_LOCK_BLOCKS, lock_value=12) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offer = swap_clients[1].getOffer(offer_id) bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.BID_RECEIVED) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED) bid, xmr_swap = swap_clients[0].getXmrBid(bid_id) assert(xmr_swap) @@ -606,8 +476,8 @@ class Test(unittest.TestCase): swap_clients[0].acceptXmrBid(bid_id) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180) - self.wait_for_bid(swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True) js_w0_after = json.loads(urlopen('http://localhost:1800/json/wallets').read()) print('[rm] js_w0_before', json.dumps(js_w0_before)) @@ -622,12 +492,12 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer( Coins.PART, Coins.XMR, 101 * COIN, 0.13 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP, lock_type=SEQUENCE_LOCK_BLOCKS, lock_value=12) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offer = swap_clients[1].getOffer(offer_id) bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.BID_RECEIVED) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED) bid, xmr_swap = swap_clients[0].getXmrBid(bid_id) assert(xmr_swap) @@ -637,13 +507,13 @@ class Test(unittest.TestCase): swap_clients[0].acceptXmrBid(bid_id) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.BID_ABANDONED, wait_for=180) - self.wait_for_bid(swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_SWIPED, wait_for=80, sent=True) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_ABANDONED, wait_for=180) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_SWIPED, wait_for=80, sent=True) js_w0_after = json.loads(urlopen('http://localhost:1800/json/wallets').read()) - self.wait_for_none_active(1800) - self.wait_for_none_active(1801) + wait_for_none_active(delay_event, 1800) + wait_for_none_active(delay_event, 1801) def test_04_follower_recover_b_lock_tx(self): logging.info('---------- Test PART to XMR follower recovers coin b lock tx') @@ -653,12 +523,12 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer( Coins.PART, Coins.XMR, 101 * COIN, 0.14 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP, lock_type=SEQUENCE_LOCK_BLOCKS, lock_value=18) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offer = swap_clients[1].getOffer(offer_id) bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.BID_RECEIVED) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED) bid, xmr_swap = swap_clients[0].getXmrBid(bid_id) assert(xmr_swap) @@ -667,28 +537,28 @@ class Test(unittest.TestCase): swap_clients[0].acceptXmrBid(bid_id) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180) - self.wait_for_bid(swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True) def test_05_btc_xmr(self): logging.info('---------- Test BTC to XMR') swap_clients = self.swap_clients offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 10 * COIN, 100 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers(filters={'offer_id': offer_id}) offer = offers[0] bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.BID_RECEIVED) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED) bid, xmr_swap = swap_clients[0].getXmrBid(bid_id) assert(xmr_swap) swap_clients[0].acceptXmrBid(bid_id) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180) - self.wait_for_bid(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True) def test_06_multiple_swaps(self): logging.info('---------- Test Multiple concurrent swaps') @@ -696,9 +566,9 @@ class Test(unittest.TestCase): offer1_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 10 * COIN, 100 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP) offer2_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, 10 * COIN, 0.14 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP) - self.wait_for_offer(swap_clients[1], offer1_id) + wait_for_offer(delay_event, swap_clients[1], offer1_id) offer1 = swap_clients[1].getOffer(offer1_id) - self.wait_for_offer(swap_clients[1], offer2_id) + wait_for_offer(delay_event, swap_clients[1], offer2_id) offer2 = swap_clients[1].getOffer(offer2_id) bid1_id = swap_clients[1].postXmrBid(offer1_id, offer1.amount_from) @@ -706,40 +576,40 @@ class Test(unittest.TestCase): offer3_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, 11 * COIN, 0.15 * XMR_COIN, 11 * COIN, SwapTypes.XMR_SWAP) - self.wait_for_bid(swap_clients[0], bid1_id, BidStates.BID_RECEIVED) + wait_for_bid(delay_event, swap_clients[0], bid1_id, BidStates.BID_RECEIVED) swap_clients[0].acceptXmrBid(bid1_id) - self.wait_for_offer(swap_clients[1], offer3_id) + wait_for_offer(delay_event, swap_clients[1], offer3_id) offer3 = swap_clients[1].getOffer(offer3_id) bid3_id = swap_clients[1].postXmrBid(offer3_id, offer3.amount_from) - self.wait_for_bid(swap_clients[0], bid2_id, BidStates.BID_RECEIVED) + wait_for_bid(delay_event, swap_clients[0], bid2_id, BidStates.BID_RECEIVED) swap_clients[0].acceptXmrBid(bid2_id) - self.wait_for_bid(swap_clients[0], bid3_id, BidStates.BID_RECEIVED) + wait_for_bid(delay_event, swap_clients[0], bid3_id, BidStates.BID_RECEIVED) swap_clients[0].acceptXmrBid(bid3_id) - self.wait_for_bid(swap_clients[0], bid1_id, BidStates.SWAP_COMPLETED, wait_for=180) - self.wait_for_bid(swap_clients[1], bid1_id, BidStates.SWAP_COMPLETED, sent=True) + wait_for_bid(delay_event, swap_clients[0], bid1_id, BidStates.SWAP_COMPLETED, wait_for=180) + wait_for_bid(delay_event, swap_clients[1], bid1_id, BidStates.SWAP_COMPLETED, sent=True) - self.wait_for_bid(swap_clients[0], bid2_id, BidStates.SWAP_COMPLETED, wait_for=120) - self.wait_for_bid(swap_clients[1], bid2_id, BidStates.SWAP_COMPLETED, sent=True) + wait_for_bid(delay_event, swap_clients[0], bid2_id, BidStates.SWAP_COMPLETED, wait_for=120) + wait_for_bid(delay_event, swap_clients[1], bid2_id, BidStates.SWAP_COMPLETED, sent=True) - self.wait_for_bid(swap_clients[0], bid3_id, BidStates.SWAP_COMPLETED, wait_for=120) - self.wait_for_bid(swap_clients[1], bid3_id, BidStates.SWAP_COMPLETED, sent=True) + wait_for_bid(delay_event, swap_clients[0], bid3_id, BidStates.SWAP_COMPLETED, wait_for=120) + wait_for_bid(delay_event, swap_clients[1], bid3_id, BidStates.SWAP_COMPLETED, sent=True) - self.wait_for_none_active(1800) - self.wait_for_none_active(1801) + wait_for_none_active(delay_event, 1800) + wait_for_none_active(delay_event, 1801) def test_07_revoke_offer(self): logging.info('---------- Test offer revocaction') swap_clients = self.swap_clients offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 10 * COIN, 100 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) swap_clients[0].revokeOffer(offer_id) - self.wait_for_no_offer(swap_clients[1], offer_id) + wait_for_no_offer(delay_event, swap_clients[1], offer_id) def test_08_withdraw(self): logging.info('---------- Test xmr withdrawals') @@ -753,24 +623,24 @@ class Test(unittest.TestCase): logging.info('---------- Test BTC to XMR auto accept') swap_clients = self.swap_clients offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 11 * COIN, 101 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP, auto_accept_bids=True) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offer = swap_clients[1].listOffers(filters={'offer_id': offer_id})[0] bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180) - self.wait_for_bid(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180) + wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True) def test_10_locked_refundtx(self): logging.info('---------- Test Refund tx is locked') swap_clients = self.swap_clients offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 10 * COIN, 100 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP) - self.wait_for_offer(swap_clients[1], offer_id) + wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers(filters={'offer_id': offer_id}) offer = offers[0] bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.BID_RECEIVED) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED) bid, xmr_swap = swap_clients[0].getXmrBid(bid_id) assert(xmr_swap) @@ -779,7 +649,7 @@ class Test(unittest.TestCase): swap_clients[0].acceptXmrBid(bid_id) - self.wait_for_bid(swap_clients[0], bid_id, BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED, wait_for=180) + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED, wait_for=180) bid, xmr_swap = swap_clients[0].getXmrBid(bid_id) assert(xmr_swap)