diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 2cdde06..3fadff3 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -576,9 +576,9 @@ class BasicSwap(BaseApp): if self.use_tor_proxy: have_cc_tor_opt = 'use_tor' in chain_client_settings if have_cc_tor_opt and chain_client_settings['use_tor'] is False: - self.log.warning('use_tor is true for system but false for ' + coin + '.') + self.log.warning(f'use_tor is true for system but false for {coin.name}.') elif have_cc_tor_opt is False and is_private_ip_address(node_host): - self.log.warning(f'Not using proxy for {coin} node at private ip address {node_host}.') + self.log.warning(f'Not using proxy for {coin.name} node at private ip address {node_host}.') else: proxy_host = self.tor_proxy_host proxy_port = self.tor_proxy_port diff --git a/basicswap/config.py b/basicswap/config.py index 1fb3511..cad16b1 100644 --- a/basicswap/config.py +++ b/basicswap/config.py @@ -36,7 +36,3 @@ NAMECOIN_TX = os.getenv('NAMECOIN_TX', 'namecoin-tx' + bin_suffix) XMR_BINDIR = os.path.expanduser(os.getenv('XMR_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'monero'))) XMRD = os.getenv('XMRD', 'monerod' + bin_suffix) XMR_WALLET_RPC = os.getenv('XMR_WALLET_RPC', 'monero-wallet-rpc' + bin_suffix) - -WOW_BINDIR = os.path.expanduser(os.getenv('WOW_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'wownero'))) -WOWD = os.getenv('WOWD', 'wownerod' + bin_suffix) -WOW_WALLET_RPC = os.getenv('WOW_WALLET_RPC', 'wownero-wallet-rpc' + bin_suffix) diff --git a/basicswap/http_server.py b/basicswap/http_server.py index ad5cadf..c4d6679 100644 --- a/basicswap/http_server.py +++ b/basicswap/http_server.py @@ -339,7 +339,7 @@ class HttpHandler(BaseHTTPRequestHandler): coin_available = listAvailableCoins(swap_client, with_variants=False) with_xmr: bool = any(c[0] == Coins.XMR for c in coin_available) with_wow: bool = any(c[0] == Coins.WOW for c in coin_available) - coins = [(str(c[0]) + ',0', c[1]) for c in coin_available if c[0] not in (Coins.XMR, )] + coins = [(str(c[0]) + ',0', c[1]) for c in coin_available if c[0] not in (Coins.XMR, Coins.WOW)] if any(c[0] == Coins.DCR for c in coin_available): coins.append((str(int(Coins.DCR)) + ',1', 'Decred Wallet')) diff --git a/tests/basicswap/extended/test_dcr.py b/tests/basicswap/extended/test_dcr.py index ff8cad4..d5512d6 100644 --- a/tests/basicswap/extended/test_dcr.py +++ b/tests/basicswap/extended/test_dcr.py @@ -42,7 +42,6 @@ from tests.basicswap.common import ( compare_bid_states, compare_bid_states_unordered, stopDaemons, - wait_for_balance, wait_for_bid, wait_for_bid_tx_state, wait_for_offer, @@ -207,7 +206,7 @@ def run_test_bad_ptx(self, coin_from: Coins, coin_to: Coins): offerer_states = read_json_api(1800 + node_from, path) bidder_states = read_json_api(1800 + node_to, path) - if coin_to not in (Coins.XMR,): + if coin_to not in (Coins.XMR, Coins.WOW): return # Hard to get the timing right assert (compare_bid_states_unordered(offerer_states, self.states_offerer_sh[1]) is True) @@ -312,14 +311,14 @@ def run_test_ads_success_path(self, coin_from: Coins, coin_to: Coins): node_from_ci_to = swap_clients[0].ci(coin_to) max_fee: int = 10000 - if node_from_ci_to.coin_type() in (Coins.XMR, ): + if node_from_ci_to.coin_type() in (Coins.XMR, Coins.WOW): pass else: wtx = node_from_ci_to.rpc_wallet('gettransaction', [bid.xmr_b_lock_tx.spend_txid.hex(),]) assert (bid.amount_to - node_from_ci_to.make_int(wtx['details'][0]['amount']) < max_fee) node_to_ci_from = swap_clients[1].ci(coin_from) - if node_to_ci_from.coin_type() in (Coins.XMR, ): + if node_to_ci_from.coin_type() in (Coins.XMR, Coins.WOW): pass else: wtx = node_to_ci_from.rpc_wallet('gettransaction', [xmr_swap.a_lock_spend_tx_id.hex(),]) @@ -389,14 +388,14 @@ def run_test_ads_both_refund(self, coin_from: Coins, coin_to: Coins, lock_value: node_from_ci_from = swap_clients[0].ci(coin_from) max_fee: int = 10000 - if node_from_ci_from.coin_type() in (Coins.XMR, ): + if node_from_ci_from.coin_type() in (Coins.XMR, Coins.WOW): pass else: wtx = node_from_ci_from.rpc_wallet('gettransaction', [lock_refund_spend_txid.hex(),]) assert (bid.amount - node_from_ci_from.make_int(wtx['details'][0]['amount']) < max_fee) node_to_ci_to = swap_clients[1].ci(coin_to) - if node_to_ci_to.coin_type() in (Coins.XMR, ): + if node_to_ci_to.coin_type() in (Coins.XMR, Coins.WOW): pass else: wtx = node_to_ci_to.rpc_wallet('gettransaction', [bid.xmr_b_lock_tx.spend_txid.hex(),]) @@ -602,30 +601,6 @@ class Test(BaseTest): 'blocks_confirmed': 1, } - def prepare_balance(self, coin, amount: float, port_target_node: int, port_take_from_node: int, test_balance: bool = True) -> None: - delay_iterations = 20 - delay_time = 3 - coin_ticker: str = coin.name - balance_type: str = 'balance' - address_type: str = 'deposit_address' - js_w = read_json_api(port_target_node, 'wallets') - current_balance: float = float(js_w[coin_ticker][balance_type]) - if test_balance and current_balance >= amount: - return - post_json = { - 'value': amount, - 'address': js_w[coin_ticker][address_type], - 'subfee': False, - } - if coin in (Coins.XMR, ): - post_json['sweepall'] = False - json_rv = read_json_api(port_take_from_node, 'wallets/{}/withdraw'.format(coin_ticker.lower()), post_json) - assert (len(json_rv['txid']) == 64) - wait_for_amount: float = amount - if not test_balance: - wait_for_amount += current_balance - wait_for_balance(test_delay_event, 'http://127.0.0.1:{}/json/wallets/{}'.format(port_target_node, coin_ticker.lower()), balance_type, wait_for_amount, iterations=delay_iterations, delay_time=delay_time) - def test_0001_decred_address(self): logging.info('---------- Test {}'.format(self.test_coin.name)) diff --git a/tests/basicswap/extended/test_wow.py b/tests/basicswap/extended/test_wow.py new file mode 100644 index 0000000..f0c3d00 --- /dev/null +++ b/tests/basicswap/extended/test_wow.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 tecnovert +# Distributed under the MIT software license, see the accompanying +# file LICENSE or http://www.opensource.org/licenses/mit-license.php. + +import time +import logging +import os + +from basicswap.basicswap import ( + Coins, +) +import basicswap.config as cfg +from basicswap.rpc_xmr import ( + callrpc_xmr, +) +from tests.basicswap.common import ( + stopDaemons, +) +from tests.basicswap.test_xmr import BaseTest +from bin.basicswap_run import startXmrDaemon, startXmrWalletDaemon + +from tests.basicswap.extended.test_dcr import ( + run_test_ads_success_path, + run_test_ads_both_refund, + run_test_ads_swipe_refund, +) + +NUM_NODES = 3 + +WOW_BINDIR = os.path.expanduser(os.getenv('WOW_BINDIR', os.path.join(cfg.DEFAULT_TEST_BINDIR, 'wownero'))) +WOWD = os.getenv('WOWD', 'wownerod' + cfg.bin_suffix) +WOW_WALLET_RPC = os.getenv('WOW_WALLET', 'wownero-wallet-rpc' + cfg.bin_suffix) + +WOW_BASE_PORT = 54932 +WOW_BASE_RPC_PORT = 55932 +WOW_BASE_WALLET_RPC_PORT = 55952 +WOW_BASE_ZMQ_PORT = 55972 + + +def prepareWOWDataDir(datadir, node_id, conf_file): + node_dir = os.path.join(datadir, 'wow_' + 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('log-level=4\n') + fp.write('keep-fakechain=1\n') + fp.write('data-dir={}\n'.format(node_dir)) + fp.write('fixed-difficulty=1\n') + fp.write('p2p-bind-port={}\n'.format(WOW_BASE_PORT + node_id)) + fp.write('rpc-bind-port={}\n'.format(WOW_BASE_RPC_PORT + node_id)) + fp.write('p2p-bind-ip=127.0.0.1\n') + fp.write('rpc-bind-ip=127.0.0.1\n') + fp.write('prune-blockchain=1\n') + fp.write('zmq-rpc-bind-port={}\n'.format(WOW_BASE_ZMQ_PORT + node_id)) + fp.write('zmq-rpc-bind-ip=127.0.0.1\n') + + for i in range(0, NUM_NODES): + if node_id == i: + continue + fp.write('add-exclusive-node=127.0.0.1:{}\n'.format(WOW_BASE_PORT + i)) + + +def waitForWOWNode(rpc_offset, max_tries=7, auth=None): + for i in range(max_tries + 1): + try: + if auth is None: + callrpc_xmr(WOW_BASE_RPC_PORT + rpc_offset, 'get_block_count') + else: + callrpc_xmr(WOW_BASE_WALLET_RPC_PORT + rpc_offset, 'get_languages', auth=auth) + return + except Exception as ex: + if i < max_tries: + logging.warning('Can\'t connect to WOW%s RPC: %s. Retrying in %d second/s.', '' if auth is None else ' wallet', str(ex), (i + 1)) + time.sleep(i + 1) + raise ValueError('waitForWOWNode failed') + + +class Test(BaseTest): + __test__ = True + test_coin = Coins.WOW + wow_daemons = [] + wow_wallet_auth = [] + start_ltc_nodes = False + start_xmr_nodes = False + wow_addr = None + extra_wait_time = 0 + + @classmethod + def prepareExtraCoins(cls): + pass + num_blocks = 300 + cls.wow_addr = cls.callwownodewallet(cls, 1, 'get_address')['address'] + if callrpc_xmr(WOW_BASE_RPC_PORT + 1, 'get_block_count')['count'] < num_blocks: + logging.info('Mining %d Wownero blocks to %s.', num_blocks, cls.wow_addr) + callrpc_xmr(WOW_BASE_RPC_PORT + 1, 'generateblocks', {'wallet_address': cls.wow_addr, 'amount_of_blocks': num_blocks}) + logging.info('WOW blocks: %d', callrpc_xmr(WOW_BASE_RPC_PORT + 1, 'get_block_count')['count']) + + @classmethod + def tearDownClass(cls): + logging.info('Finalising Wownero Test') + super(Test, cls).tearDownClass() + + stopDaemons(cls.wow_daemons) + cls.wow_daemons.clear() + + @classmethod + def coins_loop(cls): + super(Test, cls).coins_loop() + + if cls.wow_addr is not None: + callrpc_xmr(WOW_BASE_RPC_PORT + 0, 'generateblocks', {'wallet_address': cls.wow_addr, 'amount_of_blocks': 1}) + + @classmethod + def prepareExtraDataDir(cls, i): + if not cls.restore_instance: + prepareWOWDataDir(cfg.TEST_DATADIRS, i, 'wownerod.conf') + + node_dir = os.path.join(cfg.TEST_DATADIRS, 'wow_' + str(i)) + cls.wow_daemons.append(startXmrDaemon(node_dir, WOW_BINDIR, WOWD)) + logging.info('Started %s %d', WOWD, cls.wow_daemons[-1].handle.pid) + waitForWOWNode(i) + + opts = [ + '--daemon-address=127.0.0.1:{}'.format(WOW_BASE_RPC_PORT + i), + '--no-dns', + '--rpc-bind-port={}'.format(WOW_BASE_WALLET_RPC_PORT + i), + '--wallet-dir={}'.format(os.path.join(node_dir, 'wallets')), + '--log-file={}'.format(os.path.join(node_dir, 'wallet.log')), + '--rpc-login=test{0}:test_pass{0}'.format(i), + '--wow-shared-ringdb-dir={}'.format(os.path.join(node_dir, 'shared-ringdb')), + '--allow-mismatched-daemon-version', + ] + cls.wow_daemons.append(startXmrWalletDaemon(node_dir, WOW_BINDIR, WOW_WALLET_RPC, opts=opts)) + + cls.wow_wallet_auth.append(('test{0}'.format(i), 'test_pass{0}'.format(i))) + + waitForWOWNode(i, auth=cls.wow_wallet_auth[i]) + + if not cls.restore_instance: + logging.info('Creating WOW wallet %i', i) + cls.callwownodewallet(cls, i, 'create_wallet', {'filename': 'testwallet', 'language': 'English'}) + cls.callwownodewallet(cls, i, 'open_wallet', {'filename': 'testwallet'}) + + @classmethod + def addPIDInfo(cls, sc, i): + sc.setDaemonPID(Coins.WOW, cls.wow_daemons[i].handle.pid) + + @classmethod + def addCoinSettings(cls, settings, datadir, node_id): + settings['chainclients']['wownero'] = { + 'connection_type': 'rpc', + 'manage_daemon': False, + 'rpcport': WOW_BASE_RPC_PORT + node_id, + 'walletrpcport': WOW_BASE_WALLET_RPC_PORT + node_id, + 'walletrpcuser': 'test' + str(node_id), + 'walletrpcpassword': 'test_pass' + str(node_id), + 'walletfile': 'testwallet', + 'datadir': os.path.join(datadir, 'xmr_' + str(node_id)), + 'bindir': WOW_BINDIR, + } + + def callwownodewallet(self, node_id, method, params=None): + return callrpc_xmr(WOW_BASE_WALLET_RPC_PORT + node_id, method, params, auth=self.wow_wallet_auth[node_id]) + + def test_01_ads_part_coin(self): + run_test_ads_success_path(self, Coins.PART, self.test_coin) + + def test_02_ads_coin_part(self): + # Reverse bid + run_test_ads_success_path(self, self.test_coin, Coins.PART) + + def test_03_ads_part_coin_both_refund(self): + run_test_ads_both_refund(self, Coins.PART, self.test_coin, lock_value=20) + + def test_04_ads_coin_part_both_refund(self): + # Reverse bid + run_test_ads_both_refund(self, self.test_coin, Coins.PART, lock_value=20) + + def test_05_ads_part_coin_swipe_refund(self): + run_test_ads_swipe_refund(self, Coins.PART, self.test_coin, lock_value=20) + + def test_06_ads_coin_part_swipe_refund(self): + # Reverse bid + run_test_ads_swipe_refund(self, self.test_coin, Coins.PART, lock_value=20) diff --git a/tests/basicswap/test_btc_xmr.py b/tests/basicswap/test_btc_xmr.py index 7543df0..7eb4928 100644 --- a/tests/basicswap/test_btc_xmr.py +++ b/tests/basicswap/test_btc_xmr.py @@ -96,43 +96,6 @@ class TestFunctions(BaseTest): return float(js_wallets[coin_ticker][balance_type]) + float(js_wallets[coin_ticker][unconfirmed_name]) - def prepare_balance(self, coin, amount: float, port_target_node: int, port_take_from_node: int, test_balance: bool = True) -> None: - delay_iterations = 100 if coin == Coins.NAV else 20 - delay_time = 5 if coin == Coins.NAV else 3 - if coin == Coins.PART_BLIND: - coin_ticker: str = 'PART' - balance_type: str = 'blind_balance' - address_type: str = 'stealth_address' - type_to: str = 'blind' - elif coin == Coins.PART_ANON: - coin_ticker: str = 'PART' - balance_type: str = 'anon_balance' - address_type: str = 'stealth_address' - type_to: str = 'anon' - else: - coin_ticker: str = coin.name - balance_type: str = 'balance' - address_type: str = 'deposit_address' - js_w = read_json_api(port_target_node, 'wallets') - current_balance: float = float(js_w[coin_ticker][balance_type]) - if test_balance and current_balance >= amount: - return - post_json = { - 'value': amount, - 'address': js_w[coin_ticker][address_type], - 'subfee': False, - } - if coin in (Coins.XMR, ): - post_json['sweepall'] = False - if coin in (Coins.PART_BLIND, Coins.PART_ANON): - post_json['type_to'] = type_to - json_rv = read_json_api(port_take_from_node, 'wallets/{}/withdraw'.format(coin_ticker.lower()), post_json) - assert (len(json_rv['txid']) == 64) - wait_for_amount: float = amount - if not test_balance: - wait_for_amount += current_balance - wait_for_balance(test_delay_event, 'http://127.0.0.1:{}/json/wallets/{}'.format(port_target_node, coin_ticker.lower()), balance_type, wait_for_amount, iterations=delay_iterations, delay_time=delay_time) - def do_test_01_full_swap(self, coin_from: Coins, coin_to: Coins) -> None: logging.info('---------- Test {} to {}'.format(coin_from.name, coin_to.name)) diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py index 8967c4a..48de703 100644 --- a/tests/basicswap/test_xmr.py +++ b/tests/basicswap/test_xmr.py @@ -668,6 +668,44 @@ class BaseTest(unittest.TestCase): def getXmrBalance(self, js_wallets): return float(js_wallets[Coins.XMR.name]['unconfirmed']) + float(js_wallets[Coins.XMR.name]['balance']) + def prepare_balance(self, coin, amount: float, port_target_node: int, port_take_from_node: int, test_balance: bool = True) -> None: + delay_iterations = 100 if coin == Coins.NAV else 20 + delay_time = 5 if coin == Coins.NAV else 3 + if coin == Coins.PART_BLIND: + coin_ticker: str = 'PART' + balance_type: str = 'blind_balance' + address_type: str = 'stealth_address' + type_to: str = 'blind' + elif coin == Coins.PART_ANON: + coin_ticker: str = 'PART' + balance_type: str = 'anon_balance' + address_type: str = 'stealth_address' + type_to: str = 'anon' + else: + coin_ticker: str = coin.name + balance_type: str = 'balance' + address_type: str = 'deposit_address' + js_w = read_json_api(port_target_node, 'wallets') + current_balance: float = float(js_w[coin_ticker][balance_type]) + + if test_balance and current_balance >= amount: + return + post_json = { + 'value': amount, + 'address': js_w[coin_ticker][address_type], + 'subfee': False, + } + if coin in (Coins.XMR, Coins.WOW): + post_json['sweepall'] = False + if coin in (Coins.PART_BLIND, Coins.PART_ANON): + post_json['type_to'] = type_to + json_rv = read_json_api(port_take_from_node, 'wallets/{}/withdraw'.format(coin_ticker.lower()), post_json) + assert (len(json_rv['txid']) == 64) + wait_for_amount: float = amount + if not test_balance: + wait_for_amount += current_balance + wait_for_balance(test_delay_event, 'http://127.0.0.1:{}/json/wallets/{}'.format(port_target_node, coin_ticker.lower()), balance_type, wait_for_amount, iterations=delay_iterations, delay_time=delay_time) + class Test(BaseTest): __test__ = True