From 9160bfe45214dedfd716fac84e31e0b0e0ed3e38 Mon Sep 17 00:00:00 2001
From: tecnovert <tecnovert@tecnovert.net>
Date: Thu, 18 Apr 2024 22:15:35 +0200
Subject: [PATCH] Add Decred rpc

---
 basicswap/basicswap.py                   |  3 ++
 basicswap/interface/dcr/__init__.py      |  4 +++
 basicswap/interface/{ => dcr}/dcr.py     | 11 +++++++
 basicswap/interface/dcr/rpc.py           | 39 ++++++++++++++++++++++++
 basicswap/rpc.py                         | 16 +---------
 tests/basicswap/common.py                | 14 +++++++++
 tests/basicswap/extended/test_dash.py    | 24 +++++++--------
 tests/basicswap/extended/test_dcr.py     | 27 +++++++++++-----
 tests/basicswap/extended/test_firo.py    |  4 +--
 tests/basicswap/extended/test_nav.py     |  6 ++--
 tests/basicswap/extended/test_network.py | 10 +++---
 tests/basicswap/extended/test_nmc.py     | 22 +++++++------
 tests/basicswap/extended/test_pivx.py    | 21 +++++++------
 tests/basicswap/test_xmr.py              | 12 ++++----
 14 files changed, 141 insertions(+), 72 deletions(-)
 create mode 100644 basicswap/interface/dcr/__init__.py
 rename basicswap/interface/{ => dcr}/dcr.py (77%)
 create mode 100644 basicswap/interface/dcr/rpc.py

diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py
index 07c1409..eeec95d 100644
--- a/basicswap/basicswap.py
+++ b/basicswap/basicswap.py
@@ -654,6 +654,9 @@ class BasicSwap(BaseApp):
             interface = LTCInterface(self.coin_clients[coin], self.chain, self)
             self.coin_clients[coin]['interface_mweb'] = LTCInterfaceMWEB(self.coin_clients[coin], self.chain, self)
             return interface
+        elif coin == Coins.DCR:
+            from .interface.dcr import DCRInterface
+            return DCRInterface(self.coin_clients[coin], self.chain, self)
         elif coin == Coins.NMC:
             from .interface.nmc import NMCInterface
             return NMCInterface(self.coin_clients[coin], self.chain, self)
diff --git a/basicswap/interface/dcr/__init__.py b/basicswap/interface/dcr/__init__.py
new file mode 100644
index 0000000..5f44f91
--- /dev/null
+++ b/basicswap/interface/dcr/__init__.py
@@ -0,0 +1,4 @@
+
+from .dcr import DCRInterface
+
+__all__ = ['DCRInterface',]
diff --git a/basicswap/interface/dcr.py b/basicswap/interface/dcr/dcr.py
similarity index 77%
rename from basicswap/interface/dcr.py
rename to basicswap/interface/dcr/dcr.py
index f966ef2..b5b2a9b 100644
--- a/basicswap/interface/dcr.py
+++ b/basicswap/interface/dcr/dcr.py
@@ -15,6 +15,7 @@ from basicswap.util.crypto import (
     blake256,
     ripemd160,
 )
+from basicswap.interface.dcr.rpc import make_rpc_func
 
 
 class DCRInterface(Secp256k1Interface):
@@ -41,6 +42,10 @@ class DCRInterface(Secp256k1Interface):
 
     def __init__(self, coin_settings, network, swap_client=None):
         super().__init__(network)
+        self._rpc_host = coin_settings.get('rpchost', '127.0.0.1')
+        self._rpcport = coin_settings['rpcport']
+        self._rpcauth = coin_settings['rpcauth']
+        self.rpc = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
 
     def pkh(self, pubkey: bytes) -> bytes:
         return ripemd160(blake256(pubkey))
@@ -61,3 +66,9 @@ class DCRInterface(Secp256k1Interface):
         if blake256(blake256(prefixed_data))[:4] != checksum:
             raise ValueError('Checksum mismatch')
         return prefixed_data
+
+    def testDaemonRPC(self, with_wallet=True) -> None:
+        if with_wallet:
+            self.rpc_wallet('getwalletinfo')
+        else:
+            self.rpc('getblockchaininfo')
diff --git a/basicswap/interface/dcr/rpc.py b/basicswap/interface/dcr/rpc.py
new file mode 100644
index 0000000..91b0c6c
--- /dev/null
+++ b/basicswap/interface/dcr/rpc.py
@@ -0,0 +1,39 @@
+# -*- 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 json
+import traceback
+from basicswap.rpc import Jsonrpc
+
+
+def callrpc(rpc_port, auth, method, params=[], host='127.0.0.1'):
+    try:
+        url = 'http://{}@{}:{}/'.format(auth, host, rpc_port)
+        x = Jsonrpc(url)
+        x.__handler = None
+        v = x.json_request(method, params)
+        x.close()
+        print('[rm] v', v)
+        r = json.loads(v.decode('utf-8'))
+    except Exception as ex:
+        traceback.print_exc()
+        raise ValueError('RPC server error ' + str(ex) + ', method: ' + method)
+
+    if 'error' in r and r['error'] is not None:
+        raise ValueError('RPC error ' + str(r['error']))
+
+    return r['result']
+
+
+def make_rpc_func(port, auth, host='127.0.0.1'):
+    port = port
+    auth = auth
+    host = host
+
+    def rpc_func(method, params=None):
+        nonlocal port, auth, host
+        return callrpc(port, auth, method, params, host)
+    return rpc_func
diff --git a/basicswap/rpc.py b/basicswap/rpc.py
index 8ac5561..56cbdd7 100644
--- a/basicswap/rpc.py
+++ b/basicswap/rpc.py
@@ -1,15 +1,13 @@
 # -*- coding: utf-8 -*-
 
-# Copyright (c) 2020-2023 tecnovert
+# Copyright (c) 2020-2024 tecnovert
 # Distributed under the MIT software license, see the accompanying
 # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 
 import os
-import time
 import json
 import shlex
 import urllib
-import logging
 import traceback
 import subprocess
 from xmlrpc.client import (
@@ -20,18 +18,6 @@ from xmlrpc.client import (
 from .util import jsonDecimal
 
 
-def waitForRPC(rpc_func, expect_wallet=True, max_tries=7):
-    for i in range(max_tries + 1):
-        try:
-            rpc_func('getwalletinfo' if expect_wallet else 'getblockchaininfo')
-            return
-        except Exception as ex:
-            if i < max_tries:
-                logging.warning('Can\'t connect to RPC: %s. Retrying in %d second/s.', str(ex), (i + 1))
-                time.sleep(i + 1)
-    raise ValueError('waitForRPC failed')
-
-
 class Jsonrpc():
     # __getattr__ complicates extending ServerProxy
     def __init__(self, uri, transport=None, encoding=None, verbose=False,
diff --git a/tests/basicswap/common.py b/tests/basicswap/common.py
index 4c67bb4..dbf5f5b 100644
--- a/tests/basicswap/common.py
+++ b/tests/basicswap/common.py
@@ -312,6 +312,20 @@ def make_rpc_func(node_id, base_rpc_port=BASE_RPC_PORT):
     return rpc_func
 
 
+def waitForRPC(rpc_func, delay_event, rpc_command='getwalletinfo', max_tries=7):
+    for i in range(max_tries + 1):
+        if delay_event.is_set():
+            raise ValueError('Test stopped.')
+        try:
+            rpc_func(rpc_command)
+            return
+        except Exception as ex:
+            if i < max_tries:
+                logging.warning('Can\'t connect to RPC: %s. Retrying in %d second/s.', str(ex), (i + 1))
+                delay_event.wait(i + 1)
+    raise ValueError('waitForRPC failed')
+
+
 def extract_states_from_xu_file(file_path, prefix):
     states = {}
 
diff --git a/tests/basicswap/extended/test_dash.py b/tests/basicswap/extended/test_dash.py
index 97a7dc5..b184e14 100644
--- a/tests/basicswap/extended/test_dash.py
+++ b/tests/basicswap/extended/test_dash.py
@@ -41,7 +41,6 @@ from basicswap.util.address import (
 )
 from basicswap.rpc import (
     callrpc_cli,
-    waitForRPC,
 )
 from basicswap.contrib.key import (
     ECKey,
@@ -67,6 +66,7 @@ from tests.basicswap.common import (
     BASE_RPC_PORT,
     BASE_ZMQ_PORT,
     PREFIX_SECRET_KEY_REGTEST,
+    waitForRPC,
 )
 from bin.basicswap_run import startDaemon
 
@@ -323,7 +323,7 @@ class Test(unittest.TestCase):
 
         for i in range(NUM_NODES):
             rpc = make_part_cli_rpc_func(i)
-            waitForRPC(rpc)
+            waitForRPC(rpc, delay_event)
             if i == 0:
                 rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
             elif i == 1:
@@ -340,23 +340,23 @@ class Test(unittest.TestCase):
             with open(settings_path) as fs:
                 settings = json.load(fs)
             fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
-            cls.swap_clients.append(BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i)))
-            swap_client = cls.swap_clients[-1]
-            swap_client.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
-            swap_client.setDaemonPID(Coins.DASH, cls.daemons[1].handle.pid)
-            swap_client.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
+            sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
+            cls.swap_clients.append(sc)
+            sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
+            sc.setDaemonPID(Coins.DASH, cls.daemons[1].handle.pid)
+            sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
 
-            waitForRPC(dashRpc, expect_wallet=False)
+            waitForRPC(dashRpc, delay_event, rpc_command='getblockchaininfo')
             if len(dashRpc('listwallets')) < 1:
                 dashRpc('createwallet wallet.dat')
 
-            swap_client.start()
+            sc.start()
 
-            t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, swap_client)
+            t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
             cls.http_threads.append(t)
             t.start()
 
-        waitForRPC(dashRpc)
+        waitForRPC(dashRpc, delay_event)
         num_blocks = 500
         logging.info('Mining %d dash blocks', num_blocks)
         cls.dash_addr = dashRpc('getnewaddress mining_addr')
@@ -372,7 +372,7 @@ class Test(unittest.TestCase):
         except Exception:
             logging.info('dash: segwit is not active')
 
-        waitForRPC(btcRpc)
+        waitForRPC(btcRpc, delay_event)
         cls.btc_addr = btcRpc('getnewaddress mining_addr bech32')
         logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
         btcRpc('generatetoaddress {} {}'.format(num_blocks, cls.btc_addr))
diff --git a/tests/basicswap/extended/test_dcr.py b/tests/basicswap/extended/test_dcr.py
index b8cdefe..1743ded 100644
--- a/tests/basicswap/extended/test_dcr.py
+++ b/tests/basicswap/extended/test_dcr.py
@@ -14,18 +14,18 @@ import basicswap.config as cfg
 from basicswap.basicswap import (
     Coins,
 )
-from basicswap.rpc import (
-    waitForRPC,
+from basicswap.interface.dcr.rpc import (
+    callrpc,
 )
 from tests.basicswap.common import (
     stopDaemons,
-    make_rpc_func,
+    waitForRPC,
 )
 from tests.basicswap.util import (
     REQUIRED_SETTINGS,
 )
 
-from tests.basicswap.test_xmr import BaseTest
+from tests.basicswap.test_xmr import BaseTest, test_delay_event
 from basicswap.interface.dcr import DCRInterface
 from bin.basicswap_run import startDaemon
 
@@ -40,6 +40,16 @@ DCR_BASE_PORT = 44932
 DCR_BASE_RPC_PORT = 45932
 
 
+def make_rpc_func(node_id, base_rpc_port):
+    node_id = node_id
+    auth = 'test{0}:test_pass{0}'.format(node_id)
+
+    def rpc_func(method, params=None):
+        nonlocal node_id, auth
+        return callrpc(base_rpc_port + node_id, auth, method, params)
+    return rpc_func
+
+
 def prepareDCDDataDir(datadir, node_id, conf_file, dir_prefix, base_p2p_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):
@@ -51,10 +61,11 @@ def prepareDCDDataDir(datadir, node_id, conf_file, dir_prefix, base_p2p_port, ba
         config = [
             'regnet=1\n',  # or simnet?
             'debuglevel=debug\n',
-            f'listen=127.0.0.1:{base_p2p_port}\n',
-            f'rpclisten=127.0.0.1:{base_rpc_port}\n',
+            f'listen=127.0.0.1:{base_p2p_port + node_id}\n',
+            f'rpclisten=127.0.0.1:{base_rpc_port + node_id}\n',
             f'rpcuser=test{node_id}\n',
-            f'rpcpass=test_pass{node_id}\n',]
+            f'rpcpass=test_pass{node_id}\n',
+            'notls=1\n',]
 
         for i in range(0, num_nodes):
             if node_id == i:
@@ -100,7 +111,7 @@ class Test(BaseTest):
         cls.dcr_daemons.append(startDaemon(appdata, DCR_BINDIR, DCRD, opts=extra_opts, extra_config={'add_datadir': False, 'stdout_to_file': True, 'stdout_filename': 'dcrd_stdout.log'}))
         logging.info('Started %s %d', DCRD, cls.dcr_daemons[-1].handle.pid)
 
-        waitForRPC(make_rpc_func(i, base_rpc_port=DCR_BASE_RPC_PORT), max_tries=12)
+        waitForRPC(make_rpc_func(i, base_rpc_port=DCR_BASE_RPC_PORT), test_delay_event, rpc_command='getnetworkinfo', max_tries=12)
 
     @classmethod
     def addCoinSettings(cls, settings, datadir, node_id):
diff --git a/tests/basicswap/extended/test_firo.py b/tests/basicswap/extended/test_firo.py
index d13bab0..8c6dddb 100644
--- a/tests/basicswap/extended/test_firo.py
+++ b/tests/basicswap/extended/test_firo.py
@@ -26,7 +26,6 @@ from basicswap.util import (
 )
 from basicswap.rpc import (
     callrpc_cli,
-    waitForRPC,
 )
 from tests.basicswap.util import (
     read_json_api,
@@ -37,6 +36,7 @@ from tests.basicswap.common import (
     make_rpc_func,
     TEST_HTTP_PORT,
     wait_for_offer,
+    waitForRPC,
 )
 from basicswap.interface.contrib.firo_test_framework.mininode import (
     FromHex,
@@ -139,7 +139,7 @@ class Test(BaseTest):
         cls.firo_daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, 'firo_' + str(i)), FIRO_BINDIR, FIROD, opts=extra_opts))
         logging.info('Started %s %d', FIROD, cls.firo_daemons[-1].handle.pid)
 
-        waitForRPC(make_rpc_func(i, base_rpc_port=FIRO_BASE_RPC_PORT))
+        waitForRPC(make_rpc_func(i, base_rpc_port=FIRO_BASE_RPC_PORT), test_delay_event)
 
     @classmethod
     def addPIDInfo(cls, sc, i):
diff --git a/tests/basicswap/extended/test_nav.py b/tests/basicswap/extended/test_nav.py
index 196771f..4019207 100644
--- a/tests/basicswap/extended/test_nav.py
+++ b/tests/basicswap/extended/test_nav.py
@@ -29,9 +29,6 @@ from basicswap.util import (
 from basicswap.util.address import (
     decodeWif,
 )
-from basicswap.rpc import (
-    waitForRPC,
-)
 from tests.basicswap.util import (
     read_json_api,
 )
@@ -45,6 +42,7 @@ from tests.basicswap.common import (
     wait_for_unspent,
     wait_for_in_progress,
     wait_for_bid_tx_state,
+    waitForRPC,
 )
 from basicswap.interface.contrib.nav_test_framework.mininode import (
     ToHex,
@@ -161,7 +159,7 @@ class Test(TestFunctions):
         cls.nav_daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, 'nav_' + str(i)), NAV_BINDIR, NAVD, opts=extra_opts))
         logging.info('Started %s %d', NAVD, cls.nav_daemons[-1].handle.pid)
 
-        waitForRPC(make_rpc_func(i, base_rpc_port=NAV_BASE_RPC_PORT), max_tries=12)
+        waitForRPC(make_rpc_func(i, base_rpc_port=NAV_BASE_RPC_PORT), test_delay_event, max_tries=12)
 
     @classmethod
     def addPIDInfo(cls, sc, i):
diff --git a/tests/basicswap/extended/test_network.py b/tests/basicswap/extended/test_network.py
index 71b014e..cd4c2db 100644
--- a/tests/basicswap/extended/test_network.py
+++ b/tests/basicswap/extended/test_network.py
@@ -31,7 +31,6 @@ from basicswap.util.address import (
 from basicswap.rpc import (
     callrpc,
     callrpc_cli,
-    waitForRPC,
 )
 from basicswap.contrib.key import (
     ECKey,
@@ -56,6 +55,7 @@ from tests.basicswap.common import (
     BTC_BASE_PORT,
     BTC_BASE_RPC_PORT,
     PREFIX_SECRET_KEY_REGTEST,
+    waitForRPC,
 )
 
 from bin.basicswap_run import startDaemon
@@ -211,7 +211,7 @@ class Test(unittest.TestCase):
             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)
+                waitForRPC(rpc, delay_event)
                 if i == 0:
                     rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
                 elif i == 1:
@@ -232,7 +232,7 @@ class Test(unittest.TestCase):
                 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.handle.part_daemons[-1].handle.pid)
 
-                waitForRPC(make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT))
+                waitForRPC(make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT), delay_event)
 
             logging.info('Preparing swap clients.')
             eckey = ECKey()
@@ -248,12 +248,12 @@ class Test(unittest.TestCase):
                     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))
+                cls.swap_clients.append(sc)
                 sc.setDaemonPID(Coins.BTC, cls.btc_daemons[i].handle.pid)
                 sc.setDaemonPID(Coins.PART, cls.part_daemons[i].handle.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])
+                t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
                 cls.http_threads.append(t)
                 t.start()
 
diff --git a/tests/basicswap/extended/test_nmc.py b/tests/basicswap/extended/test_nmc.py
index 70d146b..664d447 100644
--- a/tests/basicswap/extended/test_nmc.py
+++ b/tests/basicswap/extended/test_nmc.py
@@ -39,7 +39,6 @@ from basicswap.util.address import (
 )
 from basicswap.rpc import (
     callrpc_cli,
-    waitForRPC,
 )
 from basicswap.contrib.key import (
     ECKey,
@@ -63,6 +62,7 @@ from tests.basicswap.common import (
     BASE_RPC_PORT,
     BASE_ZMQ_PORT,
     PREFIX_SECRET_KEY_REGTEST,
+    waitForRPC,
 )
 from bin.basicswap_run import startDaemon
 
@@ -289,7 +289,7 @@ class Test(unittest.TestCase):
 
         for i in range(NUM_NODES):
             rpc = make_part_cli_rpc_func(i)
-            waitForRPC(rpc)
+            waitForRPC(rpc, delay_event)
             if i == 0:
                 rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
             elif i == 1:
@@ -306,17 +306,19 @@ class Test(unittest.TestCase):
             with open(settings_path) as fs:
                 settings = json.load(fs)
             fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
-            cls.swap_clients.append(BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i)))
-            cls.swap_clients[-1].setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
-            cls.swap_clients[-1].setDaemonPID(Coins.NMC, cls.daemons[1].handle.pid)
-            cls.swap_clients[-1].setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
-            cls.swap_clients[-1].start()
+            sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
+            cls.swap_clients.append(sc)
 
-            t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i])
+            sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
+            sc.setDaemonPID(Coins.NMC, cls.daemons[1].handle.pid)
+            sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
+            sc.start()
+
+            t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
             cls.http_threads.append(t)
             t.start()
 
-        waitForRPC(nmcRpc)
+        waitForRPC(nmcRpc, delay_event)
         num_blocks = 500
         logging.info('Mining %d namecoin blocks', num_blocks)
         cls.nmc_addr = nmcRpc('getnewaddress mining_addr legacy')
@@ -332,7 +334,7 @@ class Test(unittest.TestCase):
         except Exception:
             logging.info('nmc: segwit is not active')
 
-        waitForRPC(btcRpc)
+        waitForRPC(btcRpc, delay_event)
         cls.btc_addr = btcRpc('getnewaddress mining_addr bech32')
         logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
         btcRpc('generatetoaddress {} {}'.format(num_blocks, cls.btc_addr))
diff --git a/tests/basicswap/extended/test_pivx.py b/tests/basicswap/extended/test_pivx.py
index 31706fe..ff07c67 100644
--- a/tests/basicswap/extended/test_pivx.py
+++ b/tests/basicswap/extended/test_pivx.py
@@ -41,7 +41,6 @@ from basicswap.util.address import (
 )
 from basicswap.rpc import (
     callrpc_cli,
-    waitForRPC,
 )
 from basicswap.contrib.key import (
     ECKey,
@@ -67,6 +66,7 @@ from tests.basicswap.common import (
     BASE_RPC_PORT,
     BASE_ZMQ_PORT,
     PREFIX_SECRET_KEY_REGTEST,
+    waitForRPC,
 )
 from bin.basicswap_run import startDaemon
 from bin.basicswap_prepare import downloadPIVXParams
@@ -326,7 +326,7 @@ class Test(unittest.TestCase):
 
         for i in range(NUM_NODES):
             rpc = make_part_cli_rpc_func(i)
-            waitForRPC(rpc)
+            waitForRPC(rpc, delay_event)
             if i == 0:
                 rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
             elif i == 1:
@@ -343,17 +343,18 @@ class Test(unittest.TestCase):
             with open(settings_path) as fs:
                 settings = json.load(fs)
             fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
-            cls.swap_clients.append(BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i)))
-            cls.swap_clients[-1].setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
-            cls.swap_clients[-1].setDaemonPID(Coins.PIVX, cls.daemons[1].handle.pid)
-            cls.swap_clients[-1].setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
-            cls.swap_clients[-1].start()
+            sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
+            cls.swap_clients.append(sc)
+            sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
+            sc.setDaemonPID(Coins.PIVX, cls.daemons[1].handle.pid)
+            sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
+            sc.start()
 
-            t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i])
+            t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
             cls.http_threads.append(t)
             t.start()
 
-        waitForRPC(pivxRpc)
+        waitForRPC(pivxRpc, delay_event)
         num_blocks = 1352  # CHECKLOCKTIMEVERIFY soft-fork activates at (regtest) block height 1351.
         logging.info('Mining %d pivx blocks', num_blocks)
         cls.pivx_addr = pivxRpc('getnewaddress mining_addr')
@@ -369,7 +370,7 @@ class Test(unittest.TestCase):
         except Exception:
             logging.info('pivx: segwit is not active')
 
-        waitForRPC(btcRpc)
+        waitForRPC(btcRpc, delay_event)
         cls.btc_addr = btcRpc('getnewaddress mining_addr bech32')
         logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
         btcRpc('generatetoaddress {} {}'.format(num_blocks, cls.btc_addr))
diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py
index 9ad41d7..f10a688 100644
--- a/tests/basicswap/test_xmr.py
+++ b/tests/basicswap/test_xmr.py
@@ -43,7 +43,6 @@ from basicswap.util.address import (
 from basicswap.rpc import (
     callrpc,
     callrpc_cli,
-    waitForRPC,
 )
 from basicswap.rpc_xmr import (
     callrpc_xmr,
@@ -76,6 +75,7 @@ from tests.basicswap.common import (
     wait_for_none_active,
     wait_for_balance,
     wait_for_unspent,
+    waitForRPC,
     compare_bid_states,
     extract_states_from_xu_file,
     TEST_HTTP_HOST,
@@ -374,7 +374,7 @@ class BaseTest(unittest.TestCase):
                 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)
+                    waitForRPC(rpc, test_delay_event)
                     if i == 0:
                         rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
                     elif i == 1:
@@ -400,7 +400,7 @@ class BaseTest(unittest.TestCase):
                 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].handle.pid)
 
-                waitForRPC(make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT))
+                waitForRPC(make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT), test_delay_event)
 
             if cls.start_ltc_nodes:
                 for i in range(NUM_LTC_NODES):
@@ -412,7 +412,7 @@ class BaseTest(unittest.TestCase):
                     cls.ltc_daemons.append(startDaemon(os.path.join(TEST_DIR, 'ltc_' + str(i)), cfg.LITECOIN_BINDIR, cfg.LITECOIND))
                     logging.info('Started %s %d', cfg.LITECOIND, cls.part_daemons[-1].handle.pid)
 
-                    waitForRPC(make_rpc_func(i, base_rpc_port=LTC_BASE_RPC_PORT))
+                    waitForRPC(make_rpc_func(i, base_rpc_port=LTC_BASE_RPC_PORT), test_delay_event)
 
             if cls.start_xmr_nodes:
                 for i in range(NUM_XMR_NODES):
@@ -474,6 +474,7 @@ class BaseTest(unittest.TestCase):
                         cls.network_pubkey = settings['network_pubkey']
                 fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
                 sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
+                cls.swap_clients.append(sc)
                 sc.setDaemonPID(Coins.BTC, cls.btc_daemons[i].handle.pid)
                 sc.setDaemonPID(Coins.PART, cls.part_daemons[i].handle.pid)
 
@@ -486,9 +487,8 @@ class BaseTest(unittest.TestCase):
                     # Set XMR main wallet address
                     xmr_ci = sc.ci(Coins.XMR)
                     sc.setStringKV('main_wallet_addr_' + xmr_ci.coin_name().lower(), xmr_ci.getMainWalletAddress())
-                cls.swap_clients.append(sc)
 
-                t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i])
+                t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
                 cls.http_threads.append(t)
                 t.start()
             # Set future block rewards to nowhere (a random address), so wallet amounts stay constant